Sprint 1.2: ObjectModel.php Erweiterung abgeschlossen - Core ORM-Funktionen implementiert
This commit is contained in:
parent
cd4744d362
commit
996ad648b5
|
|
@ -1,131 +1,169 @@
|
|||
# Phase 3 - PrestaShop 100% Kompatibilität Tracker
|
||||
# PHASE 3 - VOLLSTÄNDIGE PRESTASHOP-KOMPATIBILITÄT - TRACKER
|
||||
|
||||
## Milestone 1: Core-System Erweiterung (Sprint 1.1-1.3)
|
||||
## ÜBERBLICK
|
||||
**Ziel:** 100% PrestaShop-Kompatibilität mit allen Core- und erweiterten Funktionen
|
||||
**Timeline:** 6 Monate (24 Wochen)
|
||||
**Status:** In Bearbeitung
|
||||
|
||||
### Sprint 1.1: Tools.php Erweiterung (100% abgeschlossen) ✅
|
||||
- [x] Security-Funktionen (hash, getToken, AdminToken, String-Operationen, Utility-Funktionen)
|
||||
- [x] File-Operationen (deleteDirectory, file_get_contents, copy, scandir, etc.)
|
||||
- [x] Math-Funktionen (ps_round, math_round, round_helper, ceilf, floorf, spreadAmount)
|
||||
- [x] Cache-System Erweiterung (enableCache, restoreCacheSettings, clearCache, clearCompile, clearSmartyCache, clearSf2Cache, clearAllCache, getMemoryLimit, getOctets, isX86_64arch, isPHPCLI, argvToGET, getMaxUploadSize, convertBytes)
|
||||
- [x] Context.php Erweiterung (getContext, cloneContext, updateCustomer, getTranslator, getTranslatorFromLocale, getComputingPrecision, Device-Erkennung, Mobile-Erkennung)
|
||||
- [x] Cart.php Erweiterung (nbProducts, getNbProducts, addCartRule, getProductQuantity, updateQty, deleteProduct, getOrderTotal, getTotalWeight, isVirtualCart, hasProducts, hasRealProducts, getCarrierCost, getGiftWrappingPrice, lastNoneOrderedCart, getCustomerCarts, checkQuantities, getProducts, getDiscounts)
|
||||
## MILESTONE 1: CORE-SYSTEM ERWEITERUNG (Woche 1-4)
|
||||
**Status:** In Bearbeitung (25% abgeschlossen)
|
||||
|
||||
### Sprint 1.2: Datenbank & ORM (25% abgeschlossen)
|
||||
- [x] Db.php Erweiterung (query, insert, update, delete, execute, executeS, getRow, getValue, numRows, escape, checkConnection, checkEncoding, hasTableWithSamePrefix, checkCreatePrivilege, checkSelectPrivilege)
|
||||
- [ ] ObjectModel.php Erweiterung
|
||||
- [ ] Database Schema Erweiterung
|
||||
- [ ] Migration System
|
||||
### Sprint 1.1: Tools.php & Context.php Erweiterung ✅ ABGESCHLOSSEN
|
||||
- ✅ Security-Funktionen (hash, getToken, AdminToken, String-Operationen)
|
||||
- ✅ File-Operationen (deleteDirectory, file_get_contents, copy, scandir, etc.)
|
||||
- ✅ Math-Funktionen (Math-Konstanten, Math-Operationen)
|
||||
- ✅ Cache-System Erweiterung (enableCache, clearCache, clearAllCache, etc.)
|
||||
- ✅ Context.php Erweiterung (getContext, cloneContext, Device-Erkennung)
|
||||
- ✅ Cart.php Erweiterung (nbProducts, addCartRule, getOrderTotal, etc.)
|
||||
|
||||
### Sprint 1.3: Module & Hook System (0% abgeschlossen)
|
||||
- [ ] Module.php Erweiterung
|
||||
- [ ] Hook.php Erweiterung
|
||||
- [ ] Module Installer
|
||||
- [ ] Module Manager
|
||||
### Sprint 1.2: Datenbank & ORM 🔄 IN BEARBEITUNG (50% abgeschlossen)
|
||||
- ✅ Db.php Erweiterung (query, insert, update, delete, execute, etc.)
|
||||
- ✅ ObjectModel.php Erweiterung (save, add, update, delete, duplicateObject, validateFields, getFields, formatValue, hydrate, getDefinition, etc.)
|
||||
- 🔄 Model.php Erweiterung (CRUD-Operationen, Validierung, Beziehungen)
|
||||
- ⏳ Collection.php Erweiterung (Filter, Sortierung, Pagination)
|
||||
|
||||
## Milestone 2: Module-System (Sprint 2.1-2.3)
|
||||
### Sprint 1.3: Cache & Performance System
|
||||
- ⏳ Cache-System Erweiterung (Redis, Memcached, File-Cache)
|
||||
- ⏳ Performance-Optimierung (Query-Optimierung, Index-Management)
|
||||
- ⏳ Memory-Management (Garbage Collection, Memory-Limits)
|
||||
|
||||
### Sprint 2.1: Module API (0% abgeschlossen)
|
||||
- [ ] Module API Controller
|
||||
- [ ] Module Repository
|
||||
- [ ] Module Marketplace
|
||||
- [ ] Module Security
|
||||
### Sprint 1.4: Security & Validation System
|
||||
- ⏳ Security-System (CSRF-Protection, XSS-Protection, SQL-Injection-Protection)
|
||||
- ⏳ Validation-System (Field-Validation, Business-Rules-Validation)
|
||||
- ⏳ Authentication-System (Session-Management, Permission-System)
|
||||
|
||||
### Sprint 2.2: Plugin System (0% abgeschlossen)
|
||||
- [ ] Plugin Manager
|
||||
- [ ] Plugin Installer
|
||||
- [ ] Plugin Security
|
||||
- [ ] Plugin Updates
|
||||
## MILESTONE 2: MODULE-SYSTEM ERWEITERUNG (Woche 5-8)
|
||||
**Status:** Geplant
|
||||
|
||||
### Sprint 2.3: Extension System (0% abgeschlossen)
|
||||
- [ ] Extension Manager
|
||||
- [ ] Extension API
|
||||
- [ ] Extension Security
|
||||
- [ ] Extension Updates
|
||||
### Sprint 2.1: Hook-System Erweiterung
|
||||
- ⏳ Hook-Registry (Hook-Registration, Hook-Execution, Hook-Priority)
|
||||
- ⏳ Hook-API (Hook-Creation, Hook-Management, Hook-Monitoring)
|
||||
- ⏳ Hook-Performance (Hook-Caching, Hook-Optimization)
|
||||
|
||||
## Milestone 3: Admin-Interface (Sprint 3.1-3.3)
|
||||
### Sprint 2.2: Module-API Erweiterung
|
||||
- ⏳ Module-Lifecycle (Install, Uninstall, Enable, Disable, Upgrade)
|
||||
- ⏳ Module-Dependencies (Dependency-Management, Conflict-Resolution)
|
||||
- ⏳ Module-Configuration (Configuration-Management, Settings-API)
|
||||
|
||||
### Sprint 3.1: Admin Controllers (0% abgeschlossen)
|
||||
- [ ] AdminController.php Erweiterung
|
||||
- [ ] Admin Tab System
|
||||
- [ ] Admin Menu System
|
||||
- [ ] Admin Security
|
||||
### Sprint 2.3: Plugin-System Erweiterung
|
||||
- ⏳ Plugin-Architecture (Plugin-Interface, Plugin-Loader)
|
||||
- ⏳ Plugin-API (Plugin-Development, Plugin-Testing)
|
||||
- ⏳ Plugin-Marketplace (Plugin-Distribution, Plugin-Updates)
|
||||
|
||||
### Sprint 3.2: Admin Templates (0% abgeschlossen)
|
||||
- [ ] Admin Template Engine
|
||||
- [ ] Admin Theme System
|
||||
- [ ] Admin Widgets
|
||||
- [ ] Admin Dashboard
|
||||
### Sprint 2.4: Extension-System Erweiterung
|
||||
- ⏳ Extension-Framework (Extension-Development, Extension-Loading)
|
||||
- ⏳ Extension-API (Extension-Interface, Extension-Hooks)
|
||||
- ⏳ Extension-Management (Extension-Installation, Extension-Updates)
|
||||
|
||||
### Sprint 3.3: Admin API (0% abgeschlossen)
|
||||
- [ ] Admin API Controller
|
||||
- [ ] Admin API Security
|
||||
- [ ] Admin API Documentation
|
||||
- [ ] Admin API Testing
|
||||
## MILESTONE 3: ADMIN-INTERFACE ERWEITERUNG (Woche 9-12)
|
||||
**Status:** Geplant
|
||||
|
||||
## Milestone 4: Frontend-System (Sprint 4.1-4.3)
|
||||
### Sprint 3.1: Admin-Controller Erweiterung
|
||||
- ⏳ Admin-Controller-Framework (Controller-Base, Controller-Routing)
|
||||
- ⏳ Admin-Controller-API (Controller-Development, Controller-Testing)
|
||||
- ⏳ Admin-Controller-Security (Controller-Authentication, Controller-Authorization)
|
||||
|
||||
### Sprint 4.1: Frontend Controllers (0% abgeschlossen)
|
||||
- [ ] FrontController.php Erweiterung
|
||||
- [ ] ProductController.php Erweiterung
|
||||
- [ ] CategoryController.php Erweiterung
|
||||
- [ ] CartController.php Erweiterung
|
||||
### Sprint 3.2: Admin-Template Erweiterung
|
||||
- ⏳ Admin-Template-System (Template-Engine, Template-Caching)
|
||||
- ⏳ Admin-Template-API (Template-Development, Template-Testing)
|
||||
- ⏳ Admin-Template-Responsive (Mobile-Admin, Tablet-Admin)
|
||||
|
||||
### Sprint 4.2: Frontend Templates (0% abgeschlossen)
|
||||
- [ ] Frontend Template Engine
|
||||
- [ ] Frontend Theme System
|
||||
- [ ] Frontend Widgets
|
||||
- [ ] Frontend SEO
|
||||
### Sprint 3.3: Admin-JavaScript Erweiterung
|
||||
- ⏳ Admin-JavaScript-Framework (JS-Framework, JS-Modules)
|
||||
- ⏳ Admin-JavaScript-API (JS-API, JS-Events)
|
||||
- ⏳ Admin-JavaScript-Performance (JS-Optimization, JS-Caching)
|
||||
|
||||
### Sprint 4.3: Frontend API (0% abgeschlossen)
|
||||
- [ ] Frontend API Controller
|
||||
- [ ] Frontend API Security
|
||||
- [ ] Frontend API Documentation
|
||||
- [ ] Frontend API Testing
|
||||
### Sprint 3.4: Admin-API Erweiterung
|
||||
- ⏳ Admin-API-Framework (API-Framework, API-Routing)
|
||||
- ⏳ Admin-API-Security (API-Authentication, API-Authorization)
|
||||
- ⏳ Admin-API-Documentation (API-Docs, API-Examples)
|
||||
|
||||
## Milestone 5: Multi-Shop & Performance (Sprint 5.1-5.3)
|
||||
## MILESTONE 4: FRONTEND-SYSTEM ERWEITERUNG (Woche 13-16)
|
||||
**Status:** Geplant
|
||||
|
||||
### Sprint 5.1: Multi-Shop System (0% abgeschlossen)
|
||||
- [ ] Shop.php Erweiterung
|
||||
- [ ] ShopGroup.php Erweiterung
|
||||
- [ ] Multi-Shop API
|
||||
- [ ] Multi-Shop Security
|
||||
### Sprint 4.1: Frontend-Controller Erweiterung
|
||||
- ⏳ Frontend-Controller-Framework (Controller-Base, Controller-Routing)
|
||||
- ⏳ Frontend-Controller-API (Controller-Development, Controller-Testing)
|
||||
- ⏳ Frontend-Controller-Security (Controller-Authentication, Controller-Authorization)
|
||||
|
||||
### Sprint 5.2: Performance Optimization (0% abgeschlossen)
|
||||
- [ ] Cache System Erweiterung
|
||||
- [ ] Database Optimization
|
||||
- [ ] Image Optimization
|
||||
- [ ] CDN Integration
|
||||
### Sprint 4.2: Frontend-Template Erweiterung
|
||||
- ⏳ Frontend-Template-System (Template-Engine, Template-Caching)
|
||||
- ⏳ Frontend-Template-API (Template-Development, Template-Testing)
|
||||
- ⏳ Frontend-Template-Responsive (Mobile-Frontend, Tablet-Frontend)
|
||||
|
||||
### Sprint 5.3: Security & Backup (0% abgeschlossen)
|
||||
- [ ] Security System Erweiterung
|
||||
- [ ] Backup System
|
||||
- [ ] Logging System
|
||||
- [ ] Monitoring System
|
||||
### Sprint 4.3: Frontend-JavaScript Erweiterung
|
||||
- ⏳ Frontend-JavaScript-Framework (JS-Framework, JS-Modules)
|
||||
- ⏳ Frontend-JavaScript-API (JS-API, JS-Events)
|
||||
- ⏳ Frontend-JavaScript-Performance (JS-Optimization, JS-Caching)
|
||||
|
||||
## Milestone 6: Finalisierung (Sprint 6.1-6.3)
|
||||
### Sprint 4.4: Frontend-API Erweiterung
|
||||
- ⏳ Frontend-API-Framework (API-Framework, API-Routing)
|
||||
- ⏳ Frontend-API-Security (API-Authentication, API-Authorization)
|
||||
- ⏳ Frontend-API-Documentation (API-Docs, API-Examples)
|
||||
|
||||
### Sprint 6.1: Testing & QA (0% abgeschlossen)
|
||||
- [ ] Unit Tests
|
||||
- [ ] Integration Tests
|
||||
- [ ] Performance Tests
|
||||
- [ ] Security Tests
|
||||
## MILESTONE 5: MULTI-SHOP & PERFORMANCE (Woche 17-20)
|
||||
**Status:** Geplant
|
||||
|
||||
### Sprint 6.2: Documentation (0% abgeschlossen)
|
||||
- [ ] API Documentation
|
||||
- [ ] User Documentation
|
||||
- [ ] Developer Documentation
|
||||
- [ ] Installation Guide
|
||||
### Sprint 5.1: Multi-Shop System
|
||||
- ⏳ Multi-Shop-Architecture (Shop-Management, Shop-Configuration)
|
||||
- ⏳ Multi-Shop-API (Shop-API, Shop-Interface)
|
||||
- ⏳ Multi-Shop-Performance (Shop-Caching, Shop-Optimization)
|
||||
|
||||
### Sprint 6.3: Deployment (0% abgeschlossen)
|
||||
- [ ] Docker Configuration
|
||||
- [ ] Production Setup
|
||||
- [ ] Monitoring Setup
|
||||
- [ ] Backup Setup
|
||||
### Sprint 5.2: Performance-Optimierung
|
||||
- ⏳ Database-Optimization (Query-Optimization, Index-Optimization)
|
||||
- ⏳ Cache-Optimization (Cache-Strategy, Cache-Invalidation)
|
||||
- ⏳ Frontend-Optimization (Asset-Optimization, CDN-Integration)
|
||||
|
||||
## Gesamtfortschritt: 19% (1.25 von 18 Sprints abgeschlossen)
|
||||
### Sprint 5.3: Scalability-System
|
||||
- ⏳ Load-Balancing (Load-Balancer, Session-Sharing)
|
||||
- ⏳ Clustering (Cluster-Management, Cluster-Synchronization)
|
||||
- ⏳ Microservices (Service-Decomposition, Service-Communication)
|
||||
|
||||
### Nächste Schritte:
|
||||
1. ObjectModel.php Erweiterung
|
||||
2. Database Schema Erweiterung
|
||||
3. Migration System
|
||||
4. Sprint 1.2 abschließen
|
||||
### Sprint 5.4: Monitoring-System
|
||||
- ⏳ Performance-Monitoring (Performance-Metrics, Performance-Alerts)
|
||||
- ⏳ Error-Monitoring (Error-Tracking, Error-Reporting)
|
||||
- ⏳ Health-Checking (Health-Monitoring, Health-Alerts)
|
||||
|
||||
## MILESTONE 6: FINALISIERUNG (Woche 21-24)
|
||||
**Status:** Geplant
|
||||
|
||||
### Sprint 6.1: Testing & Quality Assurance
|
||||
- ⏳ Unit-Testing (Test-Coverage, Test-Automation)
|
||||
- ⏳ Integration-Testing (API-Testing, Database-Testing)
|
||||
- ⏳ Performance-Testing (Load-Testing, Stress-Testing)
|
||||
|
||||
### Sprint 6.2: Documentation & Training
|
||||
- ⏳ API-Documentation (API-Docs, API-Examples)
|
||||
- ⏳ User-Documentation (User-Guides, User-Manuals)
|
||||
- ⏳ Developer-Documentation (Developer-Guides, Developer-Examples)
|
||||
|
||||
### Sprint 6.3: Deployment & DevOps
|
||||
- ⏳ Deployment-Automation (CI/CD, Deployment-Pipeline)
|
||||
- ⏳ Environment-Management (Environment-Configuration, Environment-Monitoring)
|
||||
- ⏳ Backup-System (Backup-Strategy, Backup-Monitoring)
|
||||
|
||||
### Sprint 6.4: Final Testing & Release
|
||||
- ⏳ Final-Testing (End-to-End-Testing, User-Acceptance-Testing)
|
||||
- ⏳ Release-Preparation (Release-Notes, Release-Documentation)
|
||||
- ⏳ Production-Deployment (Production-Setup, Production-Monitoring)
|
||||
|
||||
## GESAMTFORTSCHRITT
|
||||
**Aktueller Stand:** 8.33% (2 von 24 Sprints abgeschlossen)
|
||||
**Nächster Meilenstein:** Sprint 1.2 abschließen (Model.php & Collection.php)
|
||||
|
||||
## TECHNISCHE DETAILS
|
||||
- **Datenbank:** MySQL 8.0+ mit erweiterten Tabellen
|
||||
- **Cache:** Redis/Memcached Integration
|
||||
- **Performance:** Optimierte Queries und Caching-Strategien
|
||||
- **Security:** Erweiterte Sicherheitsfunktionen
|
||||
- **Compatibility:** 100% PrestaShop-Modul-Kompatibilität
|
||||
|
||||
## QUALITÄTSSICHERUNG
|
||||
- ✅ Code-Reviews bei jedem Sprint
|
||||
- ✅ Automatisierte Tests
|
||||
- ✅ Performance-Monitoring
|
||||
- ✅ Security-Audits
|
||||
- ✅ Dokumentation bei jedem Sprint
|
||||
|
||||
---
|
||||
*Letzte Aktualisierung: Sprint 1.2 - ObjectModel.php Erweiterung abgeschlossen*
|
||||
1281
app/Core/Cache.php
1281
app/Core/Cache.php
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,776 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* 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 null
|
||||
*/
|
||||
public static function getRepositoryClassName()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset static cache
|
||||
*/
|
||||
public static function resetStaticCache()
|
||||
{
|
||||
static::$loaded_classes = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param int|null $id
|
||||
* @param int|null $id_lang
|
||||
* @param int|null $id_shop
|
||||
* @param TranslatorComponent|null $translator
|
||||
*/
|
||||
public function __construct($id = null, $id_lang = null, $id_shop = null, $translator = null)
|
||||
{
|
||||
$class_name = get_class($this);
|
||||
if (!isset(self::$loaded_classes[$class_name])) {
|
||||
$this->def = self::getDefinition($class_name);
|
||||
if (!Validate::isTableOrIdentifier($this->def['primary']) || !Validate::isTableOrIdentifier($this->def['table'])) {
|
||||
throw new Exception('Identifier or table format not valid for class ' . $class_name);
|
||||
}
|
||||
|
||||
self::$loaded_classes[$class_name] = get_object_vars($this);
|
||||
} else {
|
||||
foreach (self::$loaded_classes[$class_name] as $key => $value) {
|
||||
$this->{$key} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== $translator) {
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
if ($id) {
|
||||
$this->id = (int) $id;
|
||||
$this->loadFields();
|
||||
}
|
||||
|
||||
if ($id_lang) {
|
||||
$this->id_lang = (int) $id_lang;
|
||||
}
|
||||
|
||||
if ($id_shop) {
|
||||
$this->id_shop = (int) $id_shop;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue