Newwebshop/classes/ObjectModel.php

1747 lines
48 KiB
PHP

<?php
/**
* Webshop System - ObjectModel Class
*
* @package WebshopSystem
* @author Webshop System Team
* @copyright 2024 Webshop System
* @license MIT
*/
/**
* ObjectModel - Base class for all database objects
*/
abstract class ObjectModel
{
/**
* List of field types
*/
public const TYPE_INT = 1;
public const TYPE_BOOL = 2;
public const TYPE_STRING = 3;
public const TYPE_FLOAT = 4;
public const TYPE_DATE = 5;
public const TYPE_HTML = 6;
public const TYPE_NOTHING = 7;
public const TYPE_SQL = 8;
/**
* List of data to format
*/
public const FORMAT_COMMON = 1;
public const FORMAT_LANG = 2;
public const FORMAT_SHOP = 3;
/**
* List of association types
*/
public const HAS_ONE = 1;
public const HAS_MANY = 2;
/** @var int|null Object ID */
public $id;
/** @var int|null Language ID */
protected $id_lang = null;
/** @var Language|null Language ID */
protected $lang_associated = null;
/** @var int|null Shop ID */
protected $id_shop = null;
/** @var array List of shop IDs */
public $id_shop_list = [];
/** @var bool */
protected $get_shop_from_context = true;
/** @var array|null Holds required fields for each ObjectModel class */
protected static $fieldsRequiredDatabase = null;
/** @var string */
protected $table;
/** @var string */
protected $identifier;
/** @var array */
protected $tables = [];
/** @var array Tables */
protected $webserviceParameters = [];
/** @var string Path to image directory */
protected $image_dir = null;
/** @var string file type of image files */
protected $image_format = 'jpg';
/** @var TranslatorComponent */
protected $translator;
/** @var array Contains object definition */
public static $definition = [];
/** @var array Holds compiled definitions of each ObjectModel class */
protected static $loaded_classes = [];
/** @var array Contains current object definition */
protected $def;
/** @var array|null List of specific fields to update */
protected $update_fields = null;
/** @var Db|bool An instance of the db */
protected static $db = false;
/** @var array|null List of HTML field */
public static $htmlFields = null;
/** @var bool Enables to define an ID before adding object */
public $force_id = false;
/** @var bool if true, objects are cached in memory */
protected static $cache_objects = true;
/**
* Get repository class name
*
* @return string
*/
public static function getRepositoryClassName()
{
return str_replace('Model', 'Repository', get_called_class());
}
/**
* Reset static cache
*/
public static function resetStaticCache()
{
self::$loaded_classes = [];
self::$fieldsRequiredDatabase = [];
self::$htmlFields = [];
}
/**
* Constructor
*
* @param int|null $id
* @param int|null $id_lang
* @param int|null $id_shop
* @param object|null $translator
*/
public function __construct($id = null, $id_lang = null, $id_shop = null, $translator = null)
{
$this->def = static::$definition;
$this->setDefinitionRetrocompatibility();
if ($id) {
$this->id = (int) $id;
$this->id_lang = $id_lang ? (int) $id_lang : (int) Configuration::get('PS_LANG_DEFAULT');
$this->id_shop = $id_shop ? (int) $id_shop : (int) Context::getContext()->shop->id;
$this->loadFields();
}
if ($translator) {
$this->translator = $translator;
}
if (!$this->id_lang) {
$this->id_lang = (int) Configuration::get('PS_LANG_DEFAULT');
}
if (!$this->id_shop) {
$this->id_shop = (int) Context::getContext()->shop->id;
}
}
/**
* Set definition retrocompatibility
*/
protected function setDefinitionRetrocompatibility()
{
if (!isset($this->def['table'])) {
$this->def['table'] = $this->def['classname'];
}
if (!isset($this->def['primary'])) {
$this->def['primary'] = 'id_' . $this->def['table'];
}
if (!isset($this->def['fields'])) {
$this->def['fields'] = [];
}
}
/**
* Save object
*
* @param bool $null_values
* @param bool $auto_date
* @return bool
*/
public function save($null_values = false, $auto_date = true)
{
return (int) $this->id > 0 ? $this->update($null_values) : $this->add($auto_date, $null_values);
}
/**
* Add object to database
*
* @param bool $auto_date
* @param bool $null_values
* @return bool
*/
public function add($auto_date = true, $null_values = false)
{
if (isset($this->id) && !$this->force_id) {
$this->id = null;
}
// Automatically fill dates
if ($auto_date && property_exists($this, 'date_add')) {
$this->date_add = date('Y-m-d H:i:s');
}
if ($auto_date && property_exists($this, 'date_upd')) {
$this->date_upd = date('Y-m-d H:i:s');
}
$id_shop_list = [];
if (Shop::isTableAssociated($this->def['table'])) {
$id_shop_list = Shop::getContextListShopID();
if (count($this->id_shop_list)) {
$id_shop_list = $this->id_shop_list;
}
}
// Database insertion
if (Shop::checkIdShopDefault($this->def['table']) && array_key_exists('id_shop_default', get_object_vars($this))) {
$this->id_shop_default = (in_array(Configuration::get('PS_SHOP_DEFAULT'), $id_shop_list) == true) ? Configuration::get('PS_SHOP_DEFAULT') : min($id_shop_list);
}
if (!$result = Db::getInstance()->insert($this->def['table'], $this->getFields(), $null_values)) {
return false;
}
// Get object id in database if force_id is not true
if (empty($this->id)) {
$this->id = Db::getInstance()->Insert_ID();
}
// Database insertion for multishop fields related to the object
if (Shop::isTableAssociated($this->def['table'])) {
$fields = $this->getFieldsShop();
$fields[$this->def['primary']] = (int) $this->id;
foreach ($id_shop_list as $id_shop) {
$fields['id_shop'] = (int) $id_shop;
$result &= Db::getInstance()->insert($this->def['table'] . '_shop', $fields, $null_values);
}
}
if (!$result) {
return false;
}
// Database insertion for multilingual fields related to the object
if (!empty($this->def['multilang'])) {
$fields = $this->getFieldsLang();
if ($fields && is_array($fields)) {
$shops = Shop::getCompleteListOfShopsID();
$asso = Shop::getAssoTable($this->def['table'] . '_lang');
foreach ($fields as $field) {
foreach (array_keys($field) as $key) {
if (!Validate::isTableOrIdentifier($key)) {
throw new Exception('key ' . $key . ' is not table or identifier');
}
}
$field[$this->def['primary']] = (int) $this->id;
if ($asso !== false && $asso['type'] == 'fk_shop') {
foreach ($shops as $id_shop) {
$field['id_shop'] = (int) $id_shop;
$result &= Db::getInstance()->insert($this->def['table'] . '_lang', $field);
}
} else {
$result &= Db::getInstance()->insert($this->def['table'] . '_lang', $field);
}
}
}
}
return $result;
}
/**
* Duplicate object
*
* @return ObjectModel|false
*/
public function duplicateObject()
{
$definition = self::getDefinition($this);
$res = Db::getInstance()->getRow(
'SELECT * FROM `' . _DB_PREFIX_ . $definition['table'] . '` WHERE `' . $definition['primary'] . '` = ' . (int) $this->id
);
if (!$res) {
return false;
}
unset($res[$definition['primary']]);
foreach ($res as $field => &$value) {
if (isset($definition['fields'][$field])) {
$value = self::formatValue(
$value,
$definition['fields'][$field]['type'],
false,
true,
!empty($definition['fields'][$field]['allow_null'])
);
}
}
if (!Db::getInstance()->insert($definition['table'], $res)) {
return false;
}
$object_id = Db::getInstance()->Insert_ID();
if (isset($definition['multilang']) && $definition['multilang']) {
$result = Db::getInstance()->executeS('
SELECT * FROM `' . _DB_PREFIX_ . $definition['table'] . '_lang`
WHERE `' . $definition['primary'] . '` = ' . (int) $this->id
);
if (!$result) {
return false;
}
foreach ($result as $rowKey => $row) {
foreach ($row as $field => $value) {
if (isset($definition['fields'][$field])) {
$result[$rowKey][$field] = self::formatValue(
$value,
$definition['fields'][$field]['type'],
false,
true,
!empty($definition['fields'][$field]['allow_null'])
);
}
}
}
foreach ($result as $row2) {
$row2[$definition['primary']] = (int) $object_id;
if (!Db::getInstance()->insert($definition['table'] . '_lang', $row2)) {
return false;
}
}
}
$object_duplicated = new $definition['classname']((int) $object_id);
$object_duplicated->duplicateShops((int) $this->id);
return $object_duplicated;
}
/**
* Update object in database
*
* @param bool $null_values
* @return bool
*/
public function update($null_values = false)
{
$this->clearCache();
// Automatically fill dates
if (property_exists($this, 'date_upd')) {
$this->date_upd = date('Y-m-d H:i:s');
}
$id_shop_list = [];
if (Shop::isTableAssociated($this->def['table'])) {
$id_shop_list = Shop::getContextListShopID();
if (count($this->id_shop_list)) {
$id_shop_list = $this->id_shop_list;
}
}
// Database update
if (!$result = Db::getInstance()->update($this->def['table'], $this->getFields(), '`' . pSQL($this->def['primary']) . '` = ' . (int) $this->id, 0, $null_values)) {
return false;
}
// Database update for multishop fields related to the object
if (Shop::isTableAssociated($this->def['table'])) {
$fields = $this->getFieldsShop();
if (is_array($fields)) {
$fields[$this->def['primary']] = (int) $this->id;
foreach ($id_shop_list as $id_shop) {
$fields['id_shop'] = (int) $id_shop;
$where = $this->def['primary'] . ' = ' . (int) $this->id . ' AND id_shop = ' . (int) $id_shop;
$result &= Db::getInstance()->update($this->def['table'] . '_shop', $fields, $where, 0, $null_values);
}
}
}
// Database update for multilingual fields related to the object
if (!empty($this->def['multilang'])) {
$fields = $this->getFieldsLang();
if (is_array($fields)) {
$shops = Shop::getCompleteListOfShopsID();
$asso = Shop::getAssoTable($this->def['table'] . '_lang');
foreach ($fields as $field) {
$field[$this->def['primary']] = (int) $this->id;
if ($asso !== false && $asso['type'] == 'fk_shop') {
foreach ($shops as $id_shop) {
$field['id_shop'] = (int) $id_shop;
$where = $this->def['primary'] . ' = ' . (int) $this->id . ' AND id_lang = ' . (int) $field['id_lang'] . ' AND id_shop = ' . (int) $id_shop;
$result &= Db::getInstance()->update($this->def['table'] . '_lang', $field, $where, 0, $null_values);
}
} else {
$where = $this->def['primary'] . ' = ' . (int) $this->id . ' AND id_lang = ' . (int) $field['id_lang'];
$result &= Db::getInstance()->update($this->def['table'] . '_lang', $field, $where, 0, $null_values);
}
}
}
}
return $result;
}
/**
* Delete object from database
*
* @return bool
*/
public function delete()
{
$result = true;
// Remove from database
if (!$result = Db::getInstance()->delete($this->def['table'], '`' . $this->def['primary'] . '` = ' . (int) $this->id)) {
return false;
}
// Database deletion for multilingual fields related to the object
if (!empty($this->def['multilang'])) {
$result &= Db::getInstance()->delete($this->def['table'] . '_lang', '`' . $this->def['primary'] . '` = ' . (int) $this->id);
}
// Database deletion for multishop fields related to the object
if (Shop::isTableAssociated($this->def['table'])) {
$result &= Db::getInstance()->delete($this->def['table'] . '_shop', '`' . $this->def['primary'] . '` = ' . (int) $this->id);
}
$this->clearCache();
return $result;
}
/**
* Delete selection
*
* @param array $ids
* @return bool
*/
public function deleteSelection(array $ids)
{
$result = true;
foreach ($ids as $id) {
$this->id = (int) $id;
$result = $result && $this->delete();
}
return $result;
}
/**
* Validate fields
*
* @param bool $die
* @param bool $error_return
* @return bool|string
*/
public function validateFields($die = true, $error_return = false)
{
$error = true;
$this->cacheFieldsRequiredDatabase();
$required_fields = (isset($this->id) && $this->id) ? $this->getCachedFieldsRequiredDatabase($all = false) : $this->getCachedFieldsRequiredDatabase($all = true);
if ($required_fields) {
$fields = $this->getFields();
foreach ($required_fields as $field) {
if (!isset($fields[$field]) || !$fields[$field]) {
$error = $this->displayFieldName($field, get_class($this), true);
break;
}
}
}
if (is_string($error)) {
if ($die) {
throw new Exception($error);
}
return $error_return ? $error : false;
}
return true;
}
/**
* Get fields
*
* @return array
*/
public function getFields()
{
$this->validateFields();
$fields = $this->formatFields(self::FORMAT_COMMON);
foreach ($this->def['fields'] as $field => $data) {
if ($field == 'id_' . $this->def['table']) {
$fields[$field] = $this->id;
}
}
return $fields;
}
/**
* Get fields shop
*
* @return array
*/
public function getFieldsShop()
{
$fields = $this->formatFields(self::FORMAT_SHOP);
return $fields;
}
/**
* Get fields lang
*
* @return array
*/
public function getFieldsLang()
{
$fields = $this->formatFields(self::FORMAT_LANG);
return $fields;
}
/**
* Format fields
*
* @param int $type
* @param int|null $id_lang
* @return array
*/
protected function formatFields($type, $id_lang = null)
{
$fields = [];
if (in_array($type, [self::FORMAT_LANG, self::FORMAT_SHOP])) {
$fields['id_' . $this->def['table']] = (int) $this->id;
}
if ($type == self::FORMAT_LANG) {
$fields['id_lang'] = (int) $id_lang;
}
if ($type == self::FORMAT_SHOP && !Shop::isTableAssociated($this->def['table'])) {
return $fields;
}
foreach ($this->def['fields'] as $field => $data) {
if (is_array($this->update_fields) && !in_array($field, $this->update_fields)) {
continue;
}
if ($type == self::FORMAT_LANG && empty($data['lang'])) {
continue;
}
if ($type == self::FORMAT_SHOP && empty($data['shop'])) {
continue;
}
if (isset($this->{$field})) {
$fields[$field] = self::formatValue($this->{$field}, $data['type'], false, true, (isset($data['allow_null']) ? $data['allow_null'] : false));
}
}
return $fields;
}
/**
* Format value
*
* @param mixed $value
* @param int $type
* @param bool $with_quotes
* @param bool $purify
* @param bool $allow_null
* @return string
*/
public static function formatValue($value, $type, $with_quotes = false, $purify = true, $allow_null = false)
{
if ($allow_null && $value === null) {
return 'NULL';
}
switch ($type) {
case self::TYPE_INT:
return (int) $value;
case self::TYPE_BOOL:
return (int) $value;
case self::TYPE_FLOAT:
return (float) $value;
case self::TYPE_DATE:
if (!$value) {
return 'NULL';
}
if ($with_quotes) {
return '\'' . pSQL($value) . '\'';
}
return pSQL($value);
case self::TYPE_HTML:
if ($purify) {
$value = Tools::purifyHTML($value);
}
if ($with_quotes) {
return '\'' . pSQL($value, true) . '\'';
}
return pSQL($value, true);
case self::TYPE_SQL:
return $value;
case self::TYPE_NOTHING:
return $value;
case self::TYPE_STRING:
default:
if ($with_quotes) {
return '\'' . pSQL($value) . '\'';
}
return pSQL($value);
}
}
/**
* Hydrate object
*
* @param array $data
* @param int|null $id_lang
* @return ObjectModel
*/
public function hydrate(array $data, $id_lang = null)
{
foreach ($data as $key => $value) {
if (property_exists($this, $key)) {
$this->{$key} = $value;
}
}
if ($id_lang) {
$this->id_lang = (int) $id_lang;
}
return $this;
}
/**
* Hydrate collection
*
* @param string $class
* @param array $datas
* @param int|null $id_lang
* @return array
*/
public static function hydrateCollection($class, array $datas, $id_lang = null)
{
$collection = [];
$fields = [];
if ($datas) {
$fields = array_keys($datas[0]);
}
foreach ($datas as $row) {
$obj = new $class();
$obj->hydrate($row, $id_lang);
$collection[] = $obj;
}
return $collection;
}
/**
* Get definition
*
* @param string|ObjectModel $class
* @param string|null $field
* @return array
*/
public static function getDefinition($class, $field = null)
{
if (is_object($class)) {
$class = get_class($class);
}
if (!isset(self::$definition[$class])) {
throw new Exception('Definition not found for class ' . $class);
}
if ($field === null) {
return self::$definition[$class];
}
return isset(self::$definition[$class]['fields'][$field]) ? self::$definition[$class]['fields'][$field] : null;
}
/**
* Display field name
*
* @param string $field
* @param string $class
* @param bool $htmlentities
* @param Context|null $context
* @return string
*/
public static function displayFieldName($field, $class = __CLASS__, $htmlentities = true, ?Context $context = null)
{
if (!$context) {
$context = Context::getContext();
}
$definition = self::getDefinition($class);
if (isset($definition['fields'][$field]['title'])) {
$field_name = $definition['fields'][$field]['title'];
} else {
$field_name = $field;
}
return $htmlentities ? htmlentities($field_name, ENT_QUOTES, 'utf-8') : $field_name;
}
/**
* Clear cache
*
* @param bool $all
*/
public function clearCache($all = false)
{
if ($all) {
self::$loaded_classes = [];
}
}
/**
* Enable cache
*/
public static function enableCache()
{
self::$cache_objects = true;
}
/**
* Disable cache
*/
public static function disableCache()
{
self::$cache_objects = false;
}
/**
* Load fields
*/
protected function loadFields()
{
// Load object from database if object id is present
$sql = 'SELECT * FROM `' . _DB_PREFIX_ . $this->def['table'] . '` WHERE `' . $this->def['primary'] . '` = ' . (int) $this->id;
$result = Db::getInstance()->getRow($sql);
if ($result) {
$this->hydrate($result);
}
}
/**
* Get fully qualified name
*
* @return string
*/
private function getFullyQualifiedName()
{
return str_replace('\\', '', get_class($this));
}
/**
* Get object name
*
* @return string
*/
public function getObjectName()
{
return get_class($this);
}
/**
* Validate fields required database
*
* @param bool $htmlentities
* @return bool|string
*/
public function validateFieldsRequiredDatabase($htmlentities = true)
{
$errors = [];
$required_fields = $this->getFieldsRequiredDatabase();
foreach ($required_fields as $field) {
if (!$this->validateField($field, $this->{$field}, null, [], $htmlentities)) {
$errors[] = $this->trans(
'The field %field_name% is required.',
['%field_name%' => self::displayFieldName($field, get_class($this), $htmlentities)],
'Admin.Notifications.Error'
);
}
}
if (count($errors)) {
return $htmlentities ? implode('<br />', $errors) : implode("\n", $errors);
}
return true;
}
/**
* Get fields required database
*
* @param bool $all
* @return array
*/
public function getFieldsRequiredDatabase($all = false)
{
if (!$all && isset($this->id)) {
return [];
}
$this->cacheFieldsRequiredDatabase($all);
$objectName = $this->getObjectName();
return !empty(self::$fieldsRequiredDatabase[$objectName])
? self::$fieldsRequiredDatabase[$objectName]
: [];
}
/**
* Check if field is required
*
* @param string $field_name
* @param bool $all
* @return bool
*/
public function isFieldRequired($field_name, $all = false)
{
$required_fields = $this->getFieldsRequiredDatabase($all);
return in_array($field_name, $required_fields);
}
/**
* Cache fields required database
*
* @param bool $all
*/
public function cacheFieldsRequiredDatabase($all = true)
{
if (!is_array(self::$fieldsRequiredDatabase)) {
self::$fieldsRequiredDatabase = [];
}
if ($all && !self::$fieldsRequiredDatabase) {
$result = Db::getInstance()->executeS('SELECT * FROM ' . _DB_PREFIX_ . 'required_field');
foreach ($result as $row) {
self::$fieldsRequiredDatabase[$row['object_name']][] = $row['field_name'];
}
}
}
/**
* Get cached fields required database
*
* @param bool $all
* @return array
*/
public function getCachedFieldsRequiredDatabase($all = false)
{
$this->cacheFieldsRequiredDatabase($all);
if ($all) {
return self::$fieldsRequiredDatabase;
}
$objectName = $this->getObjectName();
return !empty(self::$fieldsRequiredDatabase[$objectName])
? self::$fieldsRequiredDatabase[$objectName]
: [];
}
/**
* Add fields required database
*
* @param array $fields
* @return bool
*/
public function addFieldsRequiredDatabase($fields)
{
if (!is_array($fields)) {
return false;
}
$objectName = $this->getObjectName();
if (!Db::getInstance()->execute(
'DELETE FROM ' . _DB_PREFIX_ . 'required_field'
. " WHERE object_name = '" . Db::getInstance()->escape($objectName) . "'"
)) {
return false;
}
foreach ($fields as $field) {
if (!Db::getInstance()->insert(
'required_field',
['object_name' => $objectName, 'field_name' => pSQL($field)]
)) {
return false;
}
}
return true;
}
/**
* Check if object is associated to shop
*
* @param int|null $id_shop
* @return bool
*/
public function isAssociatedToShop($id_shop = null)
{
if ($id_shop === null) {
$id_shop = Context::getContext()->shop->id;
}
$cache_id = 'objectmodel_shop_' . $this->def['classname'] . '_' . (int) $this->id . '-' . (int) $id_shop;
if (!ObjectModel::$cache_objects || !Cache::isStored($cache_id)) {
$associated = (bool) Db::getInstance()->getValue(
'
SELECT id_shop
FROM `' . pSQL(_DB_PREFIX_ . $this->def['table']) . '_shop`
WHERE `' . $this->def['primary'] . '` = ' . (int) $this->id . '
AND id_shop = ' . (int) $id_shop
);
if (!ObjectModel::$cache_objects) {
return $associated;
}
Cache::store($cache_id, $associated);
return $associated;
}
return Cache::retrieve($cache_id);
}
/**
* Associate object to shops
*
* @param int|array $id_shops
* @return bool|void
*/
public function associateTo($id_shops)
{
if (!$this->id) {
return;
}
if (!is_array($id_shops)) {
$id_shops = [$id_shops];
}
$data = [];
foreach ($id_shops as $id_shop) {
if (!$this->isAssociatedToShop($id_shop)) {
$data[] = [
$this->def['primary'] => (int) $this->id,
'id_shop' => (int) $id_shop,
];
}
}
if ($data) {
return Db::getInstance()->insert($this->def['table'] . '_shop', $data);
}
return true;
}
/**
* Get associated shops
*
* @return array
*/
public function getAssociatedShops()
{
if (!Shop::isTableAssociated($this->def['table'])) {
return [];
}
$list = [];
$sql = 'SELECT id_shop FROM `' . _DB_PREFIX_ . $this->def['table'] . '_shop` WHERE `' . $this->def['primary'] . '` = ' . (int) $this->id;
foreach (Db::getInstance()->executeS($sql) as $row) {
$list[] = (int) $row['id_shop'];
}
return $list;
}
/**
* Duplicate shops
*
* @param int $id
* @return bool|void
*/
public function duplicateShops($id)
{
if (!Shop::isTableAssociated($this->def['table'])) {
return false;
}
$sql = 'SELECT id_shop
FROM ' . _DB_PREFIX_ . $this->def['table'] . '_shop
WHERE ' . $this->def['primary'] . ' = ' . (int) $id;
if ($results = Db::getInstance()->executeS($sql)) {
$ids = [];
foreach ($results as $row) {
$ids[] = $row['id_shop'];
}
return $this->associateTo($ids);
}
return false;
}
/**
* Check if object is multishop
*
* @return bool
*/
public function isMultishop()
{
return Shop::isTableAssociated($this->def['table']) || !empty($this->def['multishop']);
}
/**
* Check if field is multishop
*
* @param string $field
* @return bool
*/
public function isMultiShopField($field)
{
return (isset($this->def['fields'][$field]['shop']) && $this->def['fields'][$field]['shop']);
}
/**
* Check if object is lang multishop
*
* @return bool
*/
public function isLangMultishop()
{
return !empty($this->def['multilang_shop']);
}
/**
* Update multishop table
*
* @param string $classname
* @param array $data
* @param string $where
* @return bool
*/
public static function updateMultishopTable($classname, $data, $where = '')
{
$def = ObjectModel::getDefinition($classname);
if (!isset($def['fields'])) {
return false;
}
$update_fields = [];
foreach ($def['fields'] as $field_name => $field) {
if (isset($field['shop']) && $field['shop'] && isset($data[$field_name])) {
$update_fields[] = '`' . bqSQL($field_name) . '` = \'' . pSQL($data[$field_name]) . '\'';
}
}
if (!count($update_fields)) {
return true;
}
return Db::getInstance()->execute('
UPDATE `' . bqSQL(_DB_PREFIX_ . $def['table']) . '_shop`
SET ' . implode(', ', $update_fields) . '
WHERE 1 ' . $where
);
}
/**
* Get shop ID
*
* @return int
*/
public function getShopId(): int
{
return (int) Context::getContext()->shop->id;
}
/**
* Delete image
*
* @param bool $force_delete
* @return bool
*/
public function deleteImage($force_delete = false)
{
if (!$this->id) {
return false;
}
if ($force_delete || !$this->hasMultishopEntries()) {
/* Deleting object images and thumbnails (cache) */
if ($this->image_dir) {
if (file_exists($this->image_dir . $this->id . '.' . $this->image_format)
&& !unlink($this->image_dir . $this->id . '.' . $this->image_format)) {
return false;
}
}
if (isset($this->def['image']['fields'])) {
foreach ($this->def['image']['fields'] as $field) {
if (file_exists($this->image_dir . $this->id . '-' . stripslashes($field) . '.' . $this->image_format)
&& !unlink($this->image_dir . $this->id . '-' . stripslashes($field) . '.' . $this->image_format)) {
return false;
}
}
}
$types = ImageType::getImagesTypes();
foreach ($types as $image_type) {
if (file_exists($this->image_dir . $this->id . '-' . stripslashes($image_type['name']) . '.' . $this->image_format)
&& !unlink($this->image_dir . $this->id . '-' . stripslashes($image_type['name']) . '.' . $this->image_format)) {
return false;
}
}
}
return true;
}
/**
* Check if entity exists in database
*
* @param int $id_entity
* @param string|null $table
* @return bool
*/
public static function existsInDatabase($id_entity, $table = null)
{
if ($table === null) {
$table = self::$definition[get_called_class()]['table'];
}
$sql = 'SELECT `id_' . bqSQL($table) . '`
FROM `' . bqSQL(_DB_PREFIX_ . $table) . '`
WHERE `id_' . bqSQL($table) . '` = ' . (int) $id_entity;
return (bool) Db::getInstance()->getValue($sql);
}
/**
* Check if object is currently used
*
* @param string|null $table
* @param bool $has_active_column
* @return bool
*/
public static function isCurrentlyUsed($table = null, $has_active_column = false)
{
if ($table === null) {
$table = self::$definition[get_called_class()]['table'];
}
$sql = 'SELECT `id_' . bqSQL($table) . '` FROM `' . bqSQL(_DB_PREFIX_ . $table) . '`';
if ($has_active_column) {
$sql .= ' WHERE `active` = 1';
}
return (bool) Db::getInstance()->getValue($sql);
}
/**
* Validate fields lang
*
* @param bool $die
* @param bool $errorReturn
* @return bool|string
*/
public function validateFieldsLang($die = true, $errorReturn = false)
{
$errors = [];
$object = $this;
/* Checking for multilingual fields validity */
foreach ($object->def['fields'] as $field => $data) {
if (empty($data['lang'])) {
continue;
}
$values = $object->getFieldsLang();
/* If the object has not been loaded in multilanguage, we load all fields from all languages */
if ($values === false) {
$values = $object->getFieldsLang((int) Configuration::get('PS_LANG_DEFAULT'));
}
/* The object is not in a language context */
if (!is_array($values)) {
continue;
}
foreach ($values as $id_lang => $value) {
if (empty($value)) {
continue;
}
$message = $this->validateField($field, $value, (int) $id_lang, [], true);
if ($message !== true) {
if ($die) {
throw new PrestaShopException('Validation error: ' . $message);
}
$errors[] = $message;
}
}
}
return $errorReturn ? $errors : (count($errors) ? $errors : true);
}
/**
* Validate controller
*
* @param bool $htmlentities
* @return array
*/
public function validateController($htmlentities = true)
{
$errors = [];
/* Checking for multilingual fields validity */
$errors = $this->validateFieldsLang($htmlentities);
if (!is_array($errors)) {
$errors = [];
}
/* Checking for fields validity */
$errors = array_merge($errors, $this->validateFields($htmlentities));
return $errors;
}
/**
* Get webservice parameters
*
* @param string|null $ws_params_attribute_name
* @return array
*/
public function getWebserviceParameters($ws_params_attribute_name = null)
{
$this->cacheFieldsRequiredDatabase();
$default_resource_parameters = [
'objectMethods' => [
'add' => 'addWs',
'update' => 'updateWs',
],
'objectNodeName' => 'object',
'objectsNodeName' => 'objects',
'objectFields' => [
'id' => [],
'associations' => [],
],
];
if (!$ws_params_attribute_name) {
$ws_params_attribute_name = 'webserviceParameters';
}
if (!isset($this->{$ws_params_attribute_name})) {
$this->{$ws_params_attribute_name} = [];
}
$resource_parameters = array_merge($default_resource_parameters, $this->{$ws_params_attribute_name});
$object_vars = get_object_vars($this);
if ($resource_parameters['objectFields'] == [] && isset($object_vars['id'])) {
$resource_parameters['objectFields']['id'] = ['sqlId' => 'id_' . $this->def['table']];
}
// Retrocompatibility
if (isset($this->def['fields'])) {
foreach ($this->def['fields'] as $field_name => $field) {
if (isset($field['size'])) {
$resource_parameters['objectFields'][$field_name]['size'] = $field['size'];
}
if (isset($field['type'])) {
$resource_parameters['objectFields'][$field_name]['type'] = $field['type'];
}
}
}
return $resource_parameters;
}
/**
* Get webservice object list
*
* @param string $sql_join
* @param string $sql_filter
* @param string $sql_sort
* @param string $sql_limit
* @return array
*/
public function getWebserviceObjectList($sql_join, $sql_filter, $sql_sort, $sql_limit)
{
$def = $this->getDefinition($this);
if (isset($def['fields'])) {
$sql_select = [];
foreach ($def['fields'] as $field_name => $field) {
$sql_select[] = 'a.' . $field_name;
}
$sql_select = implode(', ', $sql_select);
} else {
$sql_select = 'a.*';
}
$sql = 'SELECT DISTINCT ' . $sql_select . '
FROM `' . _DB_PREFIX_ . bqSQL($def['table']) . '` a
' . $sql_join . '
WHERE 1 ' . $sql_filter . '
' . ($sql_sort ? 'ORDER BY ' . $sql_sort : '') . '
' . ($sql_limit ? 'LIMIT ' . $sql_limit : '');
return Db::getInstance()->executeS($sql, true, false);
}
/**
* Get field by lang
*
* @param string $field_name
* @param int|null $id_lang
* @return mixed
*/
public function getFieldByLang($field_name, $id_lang = null)
{
if (!$id_lang) {
$id_lang = (int) Configuration::get('PS_LANG_DEFAULT');
}
$sql = 'SELECT ' . bqSQL($field_name) . '
FROM ' . _DB_PREFIX_ . bqSQL($this->def['table']) . '_lang
WHERE ' . bqSQL($this->def['primary']) . ' = ' . (int) $this->id . '
AND id_lang = ' . (int) $id_lang;
return Db::getInstance()->getValue($sql);
}
/**
* Set fields to update
*
* @param array|null $fields
*/
public function setFieldsToUpdate(?array $fields)
{
$this->update_fields = $fields;
}
/**
* Get fields to update
*
* @return array|null
*/
public function getFieldsToUpdate(): ?array
{
return $this->update_fields;
}
/**
* Get HTML fields
*
* @return array
*/
public function getHtmlFields()
{
$html_fields = [];
foreach ($this->def['fields'] as $field_name => $field) {
if (isset($field['type']) && $field['type'] == self::TYPE_HTML) {
$html_fields[] = $field_name;
}
}
return $html_fields;
}
/**
* Get shop IDs list
*
* @return array
*/
protected function getShopIdsList(): array
{
return Shop::getShops(true, null, true);
}
/**
* Check if object has multishop entries
*
* @return bool
*/
public function hasMultishopEntries()
{
if (!Shop::isTableAssociated($this->def['table']) || !Shop::isFeatureActive()) {
return false;
}
return (bool) Db::getInstance()->getValue('SELECT COUNT(*) FROM `' . _DB_PREFIX_ . $this->def['table'] . '_shop` WHERE `' . $this->def['primary'] . '` = ' . (int) $this->id);
}
/**
* Soft delete object
*
* @return bool
*/
public function softDelete()
{
if (!$this->id) {
return false;
}
if (isset($this->def['fields']['deleted'])) {
$this->deleted = true;
return $this->update();
}
return $this->delete();
}
/**
* Toggle status
*
* @return bool
*/
public function toggleStatus()
{
if (!$this->id) {
return false;
}
if (isset($this->def['fields']['active'])) {
$this->active = !$this->active;
return $this->update();
}
return false;
}
/**
* Validate field
*
* @param string $field
* @param mixed $value
* @param int|null $id_lang
* @param array $skip
* @param bool $human_errors
* @return bool|string
*/
public function validateField($field, $value, $id_lang = null, $skip = [], $human_errors = false)
{
$definition = $this->def['fields'][$field] ?? null;
if (!$definition) {
return true;
}
$required = isset($definition['required']) ? $definition['required'] : false;
$size = isset($definition['size']) ? $definition['size'] : null;
$values = isset($definition['values']) ? $definition['values'] : null;
$validate = isset($definition['validate']) ? $definition['validate'] : null;
if ($required && empty($value) && $value !== '0') {
return $human_errors ? $this->trans('The field %field_name% is required.', ['%field_name%' => self::displayFieldName($field, get_class($this))], 'Admin.Notifications.Error') : false;
}
if ($size && strlen($value) > $size) {
return $human_errors ? $this->trans('The field %field_name% is too long (%length% chars max).', ['%field_name%' => self::displayFieldName($field, get_class($this)), '%length%' => $size], 'Admin.Notifications.Error') : false;
}
if ($values && !in_array($value, $values)) {
return $human_errors ? $this->trans('The field %field_name% is invalid.', ['%field_name%' => self::displayFieldName($field, get_class($this))], 'Admin.Notifications.Error') : false;
}
if ($validate) {
if (!method_exists('Validate', $validate)) {
return $human_errors ? $this->trans('The field %field_name% is invalid.', ['%field_name%' => self::displayFieldName($field, get_class($this))], 'Admin.Notifications.Error') : false;
}
if (!Validate::$validate($value)) {
return $human_errors ? $this->trans('The field %field_name% is invalid.', ['%field_name%' => self::displayFieldName($field, get_class($this))], 'Admin.Notifications.Error') : false;
}
}
return true;
}
/**
* Get associated language
*
* @return Language
*/
public function getAssociatedLanguage(): Language
{
if (!$this->id_lang) {
$this->id_lang = (int) Configuration::get('PS_LANG_DEFAULT');
}
return new Language($this->id_lang);
}
/**
* Trans method for translations
*
* @param string $id
* @param array $parameters
* @param string|null $domain
* @param string|null $locale
* @return string
*/
protected function trans($id, array $parameters = [], $domain = null, $locale = null)
{
if ($this->translator) {
return $this->translator->trans($id, $parameters, $domain, $locale);
}
return $id;
}
/**
* Add webservice fields
*
* @param array $fields
*/
public function addWebserviceFields($fields)
{
$this->webserviceParameters['objectFields'] = array_merge(
$this->webserviceParameters['objectFields'] ?? [],
$fields
);
}
/**
* Get webservice object list with associations
*
* @param string $sql_join
* @param string $sql_filter
* @param string $sql_sort
* @param string $sql_limit
* @return array
*/
public function getWebserviceObjectListWithAssociations($sql_join, $sql_filter, $sql_sort, $sql_limit)
{
$def = $this->getDefinition($this);
$sql_select = [];
if (isset($def['fields'])) {
foreach ($def['fields'] as $field_name => $field) {
$sql_select[] = 'a.' . $field_name;
}
$sql_select = implode(', ', $sql_select);
} else {
$sql_select = 'a.*';
}
// Add associations
if (isset($def['associations'])) {
foreach ($def['associations'] as $alias => $association) {
if (isset($association['fields'])) {
foreach ($association['fields'] as $field_name => $field) {
$sql_select .= ', ' . $alias . '.' . $field_name;
}
}
}
}
$sql = 'SELECT DISTINCT ' . $sql_select . '
FROM `' . _DB_PREFIX_ . bqSQL($def['table']) . '` a
' . $sql_join . '
WHERE 1 ' . $sql_filter . '
' . ($sql_sort ? 'ORDER BY ' . $sql_sort : '') . '
' . ($sql_limit ? 'LIMIT ' . $sql_limit : '');
return Db::getInstance()->executeS($sql, true, false);
}
/**
* Get fields for webservice
*
* @return array
*/
public function getFieldsForWebservice()
{
$fields = $this->getFields();
$def = $this->getDefinition($this);
// Add multilang fields
if (isset($def['fields'])) {
foreach ($def['fields'] as $field_name => $field) {
if (isset($field['lang']) && $field['lang']) {
$fields[$field_name] = $this->getFieldByLang($field_name);
}
}
}
return $fields;
}
/**
* Validate webservice fields
*
* @param array $data
* @return bool|array
*/
public function validateWebserviceFields($data)
{
$errors = [];
$def = $this->getDefinition($this);
foreach ($data as $field => $value) {
if (isset($def['fields'][$field])) {
$validation = $this->validateField($field, $value, null, [], true);
if ($validation !== true) {
$errors[] = $validation;
}
}
}
return empty($errors) ? true : $errors;
}
/**
* Get object as array for webservice
*
* @return array
*/
public function getObjectForWebservice()
{
$object = [
'id' => $this->id,
'associations' => []
];
$fields = $this->getFieldsForWebservice();
foreach ($fields as $field => $value) {
$object[$field] = $value;
}
return $object;
}
/**
* Update object from webservice data
*
* @param array $data
* @return bool
*/
public function updateFromWebservice($data)
{
$def = $this->getDefinition($this);
foreach ($data as $field => $value) {
if (isset($def['fields'][$field]) && property_exists($this, $field)) {
$this->{$field} = $value;
}
}
return $this->update();
}
/**
* Get cache key
*
* @param string $key
* @return string
*/
protected function getCacheKey($key)
{
return 'objectmodel_' . $this->def['classname'] . '_' . $key;
}
/**
* Store cache
*
* @param string $key
* @param mixed $value
*/
protected function storeCache($key, $value)
{
if (self::$cache_objects) {
Cache::store($this->getCacheKey($key), $value);
}
}
/**
* Retrieve cache
*
* @param string $key
* @return mixed
*/
protected function retrieveCache($key)
{
if (self::$cache_objects && Cache::isStored($this->getCacheKey($key))) {
return Cache::retrieve($this->getCacheKey($key));
}
return null;
}
/**
* Clear object cache
*/
protected function clearObjectCache()
{
if ($this->id) {
Cache::clean('objectmodel_' . $this->def['classname'] . '_' . (int) $this->id . '_*');
}
}
}