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('
', $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 . '_*'); } } }