diff --git a/classes/CartRule.php b/classes/CartRule.php index fc2206932..ca13f3099 100644 --- a/classes/CartRule.php +++ b/classes/CartRule.php @@ -208,7 +208,7 @@ class CartRuleCore extends ObjectModel { $return = true; $cart_rules = new Collection('CartRule'); - $cart_rules->where('a.id_customer = '.(int)$id_customer); + $cart_rules->where('id_customer', '=', $id_customer); foreach ($cart_rules as $cart_rule) $return &= $cart_rule->delete(); return $return; diff --git a/classes/Category.php b/classes/Category.php index 03b3c100f..2943ddc9d 100644 --- a/classes/Category.php +++ b/classes/Category.php @@ -681,7 +681,8 @@ class CategoryCore extends ObjectModel $id_lang = Context::getContext()->language->id; $categories = new Collection('Category', $id_lang); - $categories->where((int)$this->nleft.' < nleft AND nright < '.(int)$this->nright); + $categories->where('nleft', '>', $this->nleft); + $categories->where('nright', '<', $this->nright); return $categories; } diff --git a/classes/Collection.php b/classes/Collection.php index a4ea466cd..31fef423e 100644 --- a/classes/Collection.php +++ b/classes/Collection.php @@ -72,6 +72,10 @@ class CollectionCore implements Iterator, ArrayAccess, Countable */ protected $total; + protected $fields = array(); + protected $alias = array(); + protected $alias_iterator = 0; + /** * @param string $classname * @param int $id_lang @@ -87,53 +91,122 @@ class CollectionCore implements Iterator, ArrayAccess, Countable else if (!isset($this->definition['primary'])) throw new PrestashopException('Miss primary in definition for class '.$this->classname); + $alias = $this->generateAlias(); $this->query = new DbQuery(); - $this->query->select('a.*'); - $this->query->from($this->definition['table'], 'a'); + $this->query->select($alias.'.*'); + $this->query->from($this->definition['table'], $alias); // If multilang, create association to lang table + // @todo create virtual association with lang in ObjectModel::getDefinition() if (isset($this->definition['multilang']) && $this->definition['multilang']) { - $this->query->select('b.*'); - $this->query->leftJoin($this->definition['table'].'_lang', 'b', 'a.'.$this->definition['primary'].' = b.'.$this->definition['primary']); + $lang_alias = $this->generateAlias('@lang'); + $this->query->select($lang_alias.'.*'); + $this->query->leftJoin($this->definition['table'].'_lang', $lang_alias, $alias.'.'.$this->definition['primary'].' = '.$lang_alias.'.'. $this->definition['primary']); if ($this->id_lang) - $this->query->where('b.id_lang = '.(int)$this->id_lang); + $this->query->where($lang_alias.'.id_lang = '.(int)$this->id_lang); } } /** * Add WHERE restriction on query * - * @param string $str + * @param string $field Field name + * @param string $operator List of operators : =, !=, <>, <, <=, >, >=, like, notlike, regexp, notregexp + * @param mixed $value * @return Collection */ - public function where($str) + public function where($field, $operator, $value) { - $this->query->where($str); + // Create WHERE clause with an array value (IN, NOT IN) + if (is_array($value)) + { + switch (strtolower($operator)) + { + case '=' : + case 'in' : + $this->query->where($this->parseField($field).' IN('.implode(', ', $this->formatValue($value, $field))); + break; + + case '!=' : + case '<>' : + case 'notin' : + $this->query->where($this->parseField($field).' NOT IN('.implode(', ', $this->formatValue($value, $field))); + break; + + default : + throw new PrestashopException('Operator not supported for array value'); + } + } + // Create WHERE clause + else + { + switch (strtolower($operator)) + { + case '=' : + case '!=' : + case '<>' : + case '>' : + case '>=' : + case '<' : + case '<=' : + case 'like' : + case 'regexp' : + $this->query->where($this->parseField($field).' '.$operator.' '.$this->formatValue($value, $field)); + break; + + case 'notlike' : + $this->query->where($this->parseField($field).' NOT LIKE '.$this->formatValue($value, $field)); + break; + + case 'notregexp' : + $this->query->where($this->parseField($field).' NOT REGEXP '.$this->formatValue($value, $field)); + break; + + default : + throw new PrestashopException('Operator not supported'); + } + } + + return $this; + } + + /** + * Add WHERE restriction on query using real SQL syntax + * + * @param string $sql + */ + public function sqlWhere($sql) + { + $this->query->where($this->parseFields($sql)); return $this; } /** * Add ORDER BY restriction on query * - * @param string $str + * @param string $field Field name + * @param string $order asc|desc * @return Collection */ - public function orderBy($str) + public function orderBy($field, $order = 'asc') { - $this->query->orderBy($str); + $order = strtolower($order); + if ($order != 'asc' && $order != 'desc') + throw new PrestashopException('Order must be asc or desc'); + $this->query->orderBy($this->parseField($field).' '.$order); return $this; } /** * Add GROUP BY restriction on query * - * @param string $str + * @param string $field Field name * @return Collection */ - public function groupBy($str) + public function groupBy($field) { - $this->query->groupBy($str); + $this->query->groupBy($this->parseField($field)); return $this; } @@ -284,4 +357,94 @@ class CollectionCore implements Iterator, ArrayAccess, Countable $this->getAll(); unset($this->results[$offset]); } + + /** + * Parse all fields with {field} syntax in a string + * + * @param string $str + * @return string + */ + protected function parseFields($str) + { + preg_match_all('#\{(([a-z0-9_]+\.)*[a-z0-9_]+)\}#i', $str, $m); + for ($i = 0, $total = count($m[0]); $i < $total; $i++) + $str = str_replace($m[0][$i], $this->parseField($m[1][$i]), $str); + return $str; + } + + /** + * Replace a field with its SQL version (E.g. manufacturer.name with a2.name) + * + * @param string $field Field name + * @return string + */ + protected function parseField($field) + { + $info = $this->getFieldInfo($field); + return $info['alias'].'.`'.$info['name'].'`'; + } + + /** + * Format a value with the type of the given field + * + * @param mixed $value + * @param string $field Field name + */ + protected function formatValue($value, $field) + { + $info = $this->getFieldInfo($field); + if (is_array($value)) + { + $results = array(); + foreach ($value as $item) + $results[] = ObjectModel::formatValue($item, $info['type'], true); + return $results; + } + return ObjectModel::formatValue($value, $info['type'], true); + } + + /** + * Obtain some informations on a field (alias, name, type, etc.) + * + * @param string $field Field name + * @return array + */ + protected function getFieldInfo($field) + { + if (!isset($this->fields[$field])) + { + $split = explode('.', $field); + $association = ''; + $definition = ObjectModel::getDefinition($this->classname); + for ($i = 0, $total_association = count($split) - 1; $i < $total_association; $i++) + { + // @todo association + } + + $fieldname = $split[$i]; + if (!isset($definition['fields'][$fieldname])) + throw new PrestashopException('Field '.$fieldname.' not found in class '.$this->classname); + + $this->fields[$field] = array( + 'name' => $fieldname, + 'association' => $association, + 'alias' => $this->generateAlias($association), + 'type' => $definition['fields'][$fieldname]['type'], + ); + } + return $this->fields[$field]; + } + + /** + * Generate uniq alias from association name + * + * @param string $association Use empty association for alias on current table + * @return string + */ + protected function generateAlias($association = '') + { + if (!isset($this->alias[$association])) + $this->alias[$association] = 'a'.$this->alias_iterator++; + return $this->alias[$association]; + } } \ No newline at end of file diff --git a/classes/LocalizationPack.php b/classes/LocalizationPack.php index 471989313..33f2dd978 100644 --- a/classes/LocalizationPack.php +++ b/classes/LocalizationPack.php @@ -183,7 +183,7 @@ class LocalizationPackCore if (!$trg->save()) { - $this->_errors = Tools::displayError('This tax rule can\'t be saved.'); + $this->_errors[] = Tools::displayError('This tax rule can\'t be saved.'); return false; } diff --git a/classes/ObjectModel.php b/classes/ObjectModel.php index 602f29bda..9b1336867 100644 --- a/classes/ObjectModel.php +++ b/classes/ObjectModel.php @@ -300,42 +300,49 @@ abstract class ObjectModelCore } // Format field value - switch ($data['type']) - { - case self::TYPE_INT : - $fields[$field] = (int)$value; - break; - - case self::TYPE_BOOL : - $fields[$field] = (int)$value; - break; - - case self::TYPE_FLOAT : - $fields[$field] = (float)$value; - break; - - case self::TYPE_DATE : - $fields[$field] = pSQL($value); - break; - - case self::TYPE_STRING : - $fields[$field] = pSQL($value); - break; - - case self::TYPE_HTML : - $fields[$field] = pSQL($value, true); - break; - - default : - if (method_exists($this, 'formatType'.$data['type'])) - $fields[$field] = $this->{'formatType'.$data['type']}($value); - break; - } + $fields[$field] = ObjectModel::formatValue($value, $data['type']); } return $fields; } + /** + * Format a data + * + * @param mixed $value + * @param int $type + */ + public static function formatValue($value, $type, $with_quotes = false) + { + 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 ($with_quotes) + return '\''.pSQL($value).'\''; + return pSQL($value); + + case self::TYPE_HTML : + if ($with_quotes) + return '\''.pSQL($value).'\''; + return pSQL($value); + + case self::TYPE_STRING : + default : + if ($with_quotes) + return '\''.pSQL($value).'\''; + return pSQL($value); + } + } + /** * Save current object to database (add or update) * @@ -1198,7 +1205,7 @@ abstract class ObjectModelCore $reflection = new ReflectionClass($class); $definition = $reflection->getStaticPropertyValue('definition'); if ($field) - return isset($definition[$field]) ? $definition[$field] : null; + return isset($definition['fields'][$field]) ? $definition['fields'][$field] : null; return $definition; } diff --git a/classes/ProductSupplier.php b/classes/ProductSupplier.php index ebe6e19bb..8a7e1ef4f 100644 --- a/classes/ProductSupplier.php +++ b/classes/ProductSupplier.php @@ -151,10 +151,10 @@ class ProductSupplierCore extends ObjectModel public static function getSupplierCollection($id_product, $group_by_supplier = true) { $suppliers = new Collection('ProductSupplier'); - $suppliers->where('a.id_product = '.(int)$id_product); + $suppliers->where('id_product', '=', $id_product); if ($group_by_supplier) - $suppliers->groupBy('a.id_supplier'); + $suppliers->groupBy('id_supplier'); return $suppliers; } diff --git a/classes/order/Order.php b/classes/order/Order.php index e54638ccd..ee965d332 100644 --- a/classes/order/Order.php +++ b/classes/order/Order.php @@ -1337,7 +1337,7 @@ class OrderCore extends ObjectModel public function getOrderPaymentCollection() { $order_payments = new Collection('OrderPayment'); - $order_payments->where('id_order = '.(int)$this->id); + $order_payments->where('id_order', '=', $this->id); return $order_payments; } @@ -1426,7 +1426,7 @@ class OrderCore extends ObjectModel public function getInvoicesCollection() { $order_invoices = new Collection('OrderInvoice'); - $order_invoices->where('id_order = '.(int)$this->id); + $order_invoices->where('id_order', '=', $this->id); return $order_invoices; } diff --git a/classes/order/OrderInvoice.php b/classes/order/OrderInvoice.php index 5a582f3af..da7d4b4c1 100644 --- a/classes/order/OrderInvoice.php +++ b/classes/order/OrderInvoice.php @@ -503,7 +503,7 @@ class OrderInvoiceCore extends ObjectModel public function getOrderPaymentCollection() { $order_payments = new Collection('OrderPayment'); - $order_payments->where('id_order_invoice = '.(int)$this->id); + $order_payments->where('id_order_invoice', '=', $this->id); return $order_payments; } } \ No newline at end of file diff --git a/classes/order/OrderPayment.php b/classes/order/OrderPayment.php index 91e18c59a..2bd557976 100644 --- a/classes/order/OrderPayment.php +++ b/classes/order/OrderPayment.php @@ -94,7 +94,7 @@ class OrderPaymentCore extends ObjectModel public static function getByInvoiceId($id_invoice) { $payments = new Collection('OrderPayment'); - $payments->where('a.id_order_invoice = '.(int)$id_invoice); + $payments->where('id_order_invoice', '=', $id_invoice); return $payments; } } diff --git a/classes/shop/GroupShop.php b/classes/shop/GroupShop.php index 9944692a0..237034d78 100644 --- a/classes/shop/GroupShop.php +++ b/classes/shop/GroupShop.php @@ -79,9 +79,9 @@ class GroupShopCore extends ObjectModel public static function getGroupShops($active = true) { $groups = new Collection('GroupShop'); - $groups->where('deleted = 0'); + $groups->where('deleted', '=', false); if ($active) - $groups->where('active = 1'); + $groups->where('active', '=', true); return $groups; } diff --git a/classes/shop/ShopUrl.php b/classes/shop/ShopUrl.php index 0593f4dbf..3dfacb81f 100644 --- a/classes/shop/ShopUrl.php +++ b/classes/shop/ShopUrl.php @@ -93,7 +93,7 @@ class ShopUrlCore extends ObjectModel { $urls = new Collection('ShopUrl'); if ($id_shop) - $urls->where('id_shop = '.(int)$id_shop); + $urls->where('id_shop', '=', $id_shop); return $urls; } diff --git a/classes/stock/StockManager.php b/classes/stock/StockManager.php index e9a0c0234..dfd22b4db 100644 --- a/classes/stock/StockManager.php +++ b/classes/stock/StockManager.php @@ -620,11 +620,12 @@ class StockManagerCore implements StockManagerInterface protected function getStockCollection($id_product, $id_product_attribute, $id_warehouse = null, $price_te = null) { $stocks = new Collection('Stock'); - $stocks->where('a.id_product = '.(int)$id_product.' AND a.id_product_attribute = '.(int)$id_product_attribute); + $stocks->where('id_product', '=', $id_product); + $stocks->where('id_product_attribute', '=', $id_product_attribute); if ($id_warehouse) - $stocks->where('a.id_warehouse = '.(int)$id_warehouse); + $stocks->where('id_warehouse', '=', $id_warehouse); if ($price_te) - $stocks->where('a.price_te = '.(float)$price_te); + $stocks->where('price_te', '=', $price_te); return $stocks; } diff --git a/classes/stock/SupplyOrder.php b/classes/stock/SupplyOrder.php index dffd8c0e8..a031f9556 100755 --- a/classes/stock/SupplyOrder.php +++ b/classes/stock/SupplyOrder.php @@ -264,7 +264,7 @@ class SupplyOrderCore extends ObjectModel public function getEntriesCollection() { $details = new Collection('SupplyOrderDetail'); - $details->where('a.id_supply_order = '.(int)$this->id); + $details->where('id_supply_order', '=', $this->id); return $details; } diff --git a/classes/stock/WarehouseProductLocation.php b/classes/stock/WarehouseProductLocation.php index 1ad70f253..34ee61183 100644 --- a/classes/stock/WarehouseProductLocation.php +++ b/classes/stock/WarehouseProductLocation.php @@ -117,7 +117,7 @@ class WarehouseProductLocationCore extends ObjectModel public static function getCollection($id_product) { $collection = new Collection('WarehouseProductLocation'); - $collection->where('a.id_product = '.(int)$id_product); + $collection->where('id_product', '=', $id_product); return $collection; } } \ No newline at end of file diff --git a/controllers/admin/AdminSupplyOrdersController.php b/controllers/admin/AdminSupplyOrdersController.php index c4fe0502d..162bd4632 100644 --- a/controllers/admin/AdminSupplyOrdersController.php +++ b/controllers/admin/AdminSupplyOrdersController.php @@ -1089,12 +1089,13 @@ class AdminSupplyOrdersControllerCore extends AdminController if (count($ids) <= 0) return; - $orders = new Collection('SupplyOrder', $id_lang = (int)Context::getContext()->language->id); - $orders->where('is_template = 0'); - $orders->where('id_supply_order IN('.implode(', ', $ids).')'); + $id_lang = Context::getContext()->language->id; + $orders = new Collection('SupplyOrder', $id_lang); + $orders->where('is_template', '=', false); + $orders->where('id_supply_order', 'in', $ids); $id_warehouse = $this->getCurrentWarehouse(); if ($id_warehouse != -1) - $orders->where('id_warehouse = '.(int)$id_warehouse); + $orders->where('id_warehouse', '=', $id_warehouse); $orders->getAll(); $csv = new CSV($orders, $this->l('supply_orders')); $csv->export();