Nội dung
- shares
- Facebook Messenger
- Gmail
- Viber
- Skype
Prestashop chỉ cho phép sử dụng một ảnh làm ảnh đại diện cho danh mục theo mặc định, nhưng nếu bạn muốn sử dụng thêm ảnh cho subcategory/category thì làm thế nào. Trong bài viết này chúng ta sẽ thêm ảnh thứ hai cho danh mục Prestashop, nó sẽ tăng tính linh hoạt bạn có thể dùng ảnh đó hiển thị trong trang subcategory.
Hiện tại mình sử dụng Prestashop phiên bản: 1.6
Download Source files
Giới thiệu
Trước khi bắt đầu, bạn sẽ hiểu ở đây chún ta nhắm vào việc viết code thêm để sửa đổi cách làm việc của Prestashop. Do vậy bạn cần hiểu biết về PHP trước khi đọc bài viết này, xem chi tiết tài liệu tại đây. Những files sau bạ cần sửa lại:
- AdminCategoriesController.php: ở đây là nơi sử lý các hành động, upload và xóa ảnh.
- Category.php: form nhập/sửa category, là phần bạn thêm trường mới và xóa file ảnh.
- category.tpl: dùng để hiển thị ảnh mới cho subcategories.
- .htaccess: cần sử dụng nếu bạn viết lại URL, nếu không ảnh sẽ không hiển thị.
Mở rộng file AdminCategoriesController
Controller cho trang sửa category trong admin là file chúng ta sẽ làm việc nhiều. Sửa trực tiếp vào file có thể là cách hay nhất khi bạn thực hiện override. Bạn Tạo file tên ‘AdminCategoriesController.php’ trong thư mục ‘/override/controllers/admin’ và chép đoạn code sau:
class AdminCategoriesController extends AdminCategoriesControllerCore { }
Thêm toàn bộ phương thức ‘renderForm’ vào class:
class AdminCategoriesController extends AdminCategoriesControllerCore { public function renderForm() { $this->initToolbar(); $obj = $this->loadObject(true); $id_shop = Context::getContext()->shop->id; $selected_categories = array((isset($obj->id_parent) && $obj->isParentCategoryAvailable($id_shop))? (int)$obj->id_parent : (int)Tools::getValue('id_parent', Category::getRootCategory()->id)); $unidentified = new Group(Configuration::get('PS_UNIDENTIFIED_GROUP')); $guest = new Group(Configuration::get('PS_GUEST_GROUP')); $default = new Group(Configuration::get('PS_CUSTOMER_GROUP')); $unidentified_group_information = sprintf($this->l('%s - All people without a valid customer account.'), '<b>'.$unidentified->name[$this->context->language->id].'</b>'); $guest_group_information = sprintf($this->l('%s - Customer who placed an order with the guest checkout.'), '<b>'.$guest->name[$this->context->language->id].'</b>'); $default_group_information = sprintf($this->l('%s - All people who have created an account on this site.'), '<b>'.$default->name[$this->context->language->id].'</b>'); if (!($obj = $this->loadObject(true))) return; $image = _PS_CAT_IMG_DIR_.$obj->id.'.jpg'; $image_url = ImageManager::thumbnail($image, $this->table.'_'.(int)$obj->id.'.'.$this->imageType, 350, $this->imageType, true, true); $image_size = file_exists($image) ? filesize($image) / 1000 : false; $this->fields_form = array( 'tinymce' => true, 'legend' => array( 'title' => $this->l('Category'), 'icon' => 'icon-tags' ), 'input' => array( array( 'type' => 'text', 'label' => $this->l('Name'), 'name' => 'name', 'lang' => true, 'required' => true, 'class' => 'copy2friendlyUrl', 'hint' => $this->l('Invalid characters:').' <>;=#{}', ), array( 'type' => 'switch', 'label' => $this->l('Displayed'), 'name' => 'active', 'required' => false, 'is_bool' => true, 'values' => array( array( 'id' => 'active_on', 'value' => 1, 'label' => $this->l('Enabled') ), array( 'id' => 'active_off', 'value' => 0, 'label' => $this->l('Disabled') ) ) ), array( 'type' => 'categories', 'label' => $this->l('Parent category'), 'name' => 'id_parent', 'tree' => array( 'id' => 'categories-tree', 'selected_categories' => $selected_categories, 'disabled_categories' => !Tools::isSubmit('add'.$this->table) ? array($this->_category->id) : null ) ), array( 'type' => 'textarea', 'label' => $this->l('Description'), 'name' => 'description', 'autoload_rte' => true, 'lang' => true, 'hint' => $this->l('Invalid characters:').' <>;=#{}' ), array( 'type' => 'file', 'label' => $this->l('Image'), 'name' => 'image', 'display_image' => true, 'image' => $image_url ? $image_url : false, 'size' => $image_size, 'delete_url' => self::$currentIndex.'&'.$this->identifier.'='.$this->_category->id.'&token='.$this->token.'&deleteImage=1', 'hint' => $this->l('Upload a category logo from your computer.'), ), array( 'type' => 'text', 'label' => $this->l('Meta title'), 'name' => 'meta_title', 'lang' => true, 'hint' => $this->l('Forbidden characters:').' <>;=#{}' ), array( 'type' => 'text', 'label' => $this->l('Meta description'), 'name' => 'meta_description', 'lang' => true, 'hint' => $this->l('Forbidden characters:').' <>;=#{}' ), array( 'type' => 'tags', 'label' => $this->l('Meta keywords'), 'name' => 'meta_keywords', 'lang' => true, 'hint' => $this->l('To add "tags," click in the field, write something, and then press "Enter."').' '.$this->l('Forbidden characters:').' <>;=#{}' ), array( 'type' => 'text', 'label' => $this->l('Friendly URL'), 'name' => 'link_rewrite', 'lang' => true, 'required' => true, 'hint' => $this->l('Only letters, numbers, underscore (_) and the minus (-) character are allowed.') ), array( 'type' => 'group', 'label' => $this->l('Group access'), 'name' => 'groupBox', 'values' => Group::getGroups(Context::getContext()->language->id), 'info_introduction' => $this->l('You now have three default customer groups.'), 'unidentified' => $unidentified_group_information, 'guest' => $guest_group_information, 'customer' => $default_group_information, 'hint' => $this->l('Mark all of the customer groups which you would like to have access to this category.') ) ), 'submit' => array( 'title' => $this->l('Save'), 'name' => 'submitAdd'.$this->table.($this->_category->is_root_category && !Tools::isSubmit('add'.$this->table) && !Tools::isSubmit('add'.$this->table.'root') ? '': 'AndBackToParent') ) ); $this->tpl_form_vars['shared_category'] = Validate::isLoadedObject($obj) && $obj->hasMultishopEntries(); $this->tpl_form_vars['PS_ALLOW_ACCENTED_CHARS_URL'] = (int)Configuration::get('PS_ALLOW_ACCENTED_CHARS_URL'); $this->tpl_form_vars['displayBackOfficeCategory'] = Hook::exec('displayBackOfficeCategory'); // Display this field only if multistore option is enabled if (Configuration::get('PS_MULTISHOP_FEATURE_ACTIVE') && Tools::isSubmit('add'.$this->table.'root')) { $this->fields_form['input'][] = array( 'type' => 'switch', 'label' => $this->l('Root Category'), 'name' => 'is_root_category', 'required' => false, 'is_bool' => true, 'values' => array( array( 'id' => 'is_root_on', 'value' => 1, 'label' => $this->l('Yes') ), array( 'id' => 'is_root_off', 'value' => 0, 'label' => $this->l('No') ) ) ); unset($this->fields_form['input'][2],$this->fields_form['input'][3]); } // Display this field only if multistore option is enabled AND there are several stores configured if (Shop::isFeatureActive()) $this->fields_form['input'][] = array( 'type' => 'shop', 'label' => $this->l('Shop association'), 'name' => 'checkBoxShopAsso', ); // remove category tree and radio button "is_root_category" if this category has the root category as parent category to avoid any conflict if ($this->_category->id_parent == Category::getTopCategory()->id && Tools::isSubmit('updatecategory')) foreach ($this->fields_form['input'] as $k => $input) if (in_array($input['name'], array('id_parent', 'is_root_category'))) unset($this->fields_form['input'][$k]); if (!($obj = $this->loadObject(true))) return; $image = ImageManager::thumbnail(_PS_CAT_IMG_DIR_.'/'.$obj->id.'.jpg', $this->table.'_'.(int)$obj->id.'.'.$this->imageType, 350, $this->imageType, true); $this->fields_value = array( 'image' => $image ? $image : false, 'size' => $image ? filesize(_PS_CAT_IMG_DIR_.'/'.$obj->id.'.jpg') / 1000 : false ); // Added values of object Group $category_groups_ids = $obj->getGroups(); $groups = Group::getGroups($this->context->language->id); // if empty $carrier_groups_ids : object creation : we set the default groups if (empty($category_groups_ids)) { $preselected = array(Configuration::get('PS_UNIDENTIFIED_GROUP'), Configuration::get('PS_GUEST_GROUP'), Configuration::get('PS_CUSTOMER_GROUP')); $category_groups_ids = array_merge($category_groups_ids, $preselected); } foreach ($groups as $group) $this->fields_value['groupBox_'.$group['id_group']] = Tools::getValue('groupBox_'.$group['id_group'], (in_array($group['id_group'], $category_groups_ids))); $this->fields_value['is_root_category'] = (bool)Tools::isSubmit('add'.$this->table.'root'); return parent::renderForm(); } }
Chú ý: dòng cuối return parent::renderForm();
đổi thành return AdminController::renderForm();
. Để chắc chắn code làm việc bạn nên xóa file cache/class_index.php để file mới được sinh ra. Sau đó đăng nhập quay trở lại vào admin và mở trang category, nếu còn hoạt động hãy thực hiện tiếp các bước tiếp theo sau đây.
Thêm trường mới
Trước hết, chúng ta cần thêm code hiển thị mọi ảnh đã upload. Để làm điều này, tìm đoạn code:
$image = _PS_CAT_IMG_DIR_.$obj->id.'.jpg'; $image_url = ImageManager::thumbnail($image, $this->table.'_'.(int)$obj->id.'.'.$this->imageType, 350, $this->imageType, true, true); $image_size = file_exists($image) ? filesize($image) / 1000 : false;
Thêm vào sau nó đoạn code này:
$image2 = _PS_CAT_IMG_DIR_.$obj->id.'_second.jpg'; $image_url2 = ImageManager::thumbnail($image2, $this->table.'_'.(int)$obj->id.'_second.'.$this->imageType, 350, $this->imageType, true, true); $image_size2 = file_exists($image2) ? filesize($image2) / 1000 : false;
Đoạn code trên copy thêm ảnh khác đơn giản chỉ là đổi tên rất dễ bắt chiếc đúng không. Phía dưới là danh sách các fields. Chúng ta cần thêm trường file để cho phép upload ảnh. Tìm tiếp:
array( 'type' => 'file', 'label' => $this->l('Image'), 'name' => 'image', 'display_image' => true, 'image' => $image_url ? $image_url : false, 'size' => $image_size, 'delete_url' => self::$currentIndex.'&'.$this->identifier.'='.$this->_category->id.'&token='.$this->token.'&deleteImage=1', 'hint' => $this->l('Upload a category logo from your computer.'), ),
Tương tự, chèn thêm sau nó đoạn code tạo file input:
array( 'type' => 'file', 'label' => $this->l('Image2'), 'name' => 'image2', 'display_image' => true, 'image' => $image_url2 ? $image_url2 : false, 'size' => $image_size2, 'delete_url' => self::$currentIndex.'&'.$this->identifier.'='.$this->_category->id.'&token='.$this->token.'&deleteImage2=1', 'hint' => $this->l('Upload a secondary category logo from your computer.'), ),
Cuối cùng thay đổi đoạn code này:
$image = ImageManager::thumbnail(_PS_CAT_IMG_DIR_.'/'.$obj->id.'.jpg', $this->table.'_'.(int)$obj->id.'.'.$this->imageType, 350, $this->imageType, true); $this->fields_value = array( 'image' => $image ? $image : false, 'size' => $image ? filesize(_PS_CAT_IMG_DIR_.'/'.$obj->id.'.jpg') / 1000 : false );
Thành:
$image = ImageManager::thumbnail(_PS_CAT_IMG_DIR_.'/'.$obj->id.'.jpg', $this->table.'_'.(int)$obj->id.'.'.$this->imageType, 350, $this->imageType, true); $image2 = ImageManager::thumbnail(_PS_CAT_IMG_DIR_.'/'.$obj->id.'_second.jpg', $this->table.'_'.(int)$obj->id.'_second.'.$this->imageType, 350, $this->imageType, true); $this->fields_value = array( 'image' => $image ? $image : false, 'size' => $image ? filesize(_PS_CAT_IMG_DIR_.'/'.$obj->id.'.jpg') / 1000 : false, 'image2' => $image2 ? $image2 : false, 'size2' => $image2 ? filesize(_PS_CAT_IMG_DIR_.'/'.$obj->id.'_second.jpg') / 1000 : false );
Như vậy chúng ta đã hoàn thành hàm renderForm. Giờ đến bước tạo phương thức postImage().
Lưu ảnh upload & xóa file
Bạn sẽ cần tạo phương thức sử lý upload ảnh thông qua bởi hàm postImages() trong Prestashop, chúng ta sẽ sửa đổi và mở rộng phương thức này. Do đó bạn copy nội dung phương thức postImages từ controller AdminCategoriesController mặc định và dán vào lớp chúng ta đang mở rộng, giống như sau:
protected function postImage($id) { $ret = parent::postImage($id); if (($id_category = (int)Tools::getValue('id_category')) && isset($_FILES) && count($_FILES) && $_FILES['image']['name'] != null && file_exists(_PS_CAT_IMG_DIR_.$id_category.'.jpg')) { $images_types = ImageType::getImagesTypes('categories'); foreach ($images_types as $k => $image_type) { ImageManager::resize( _PS_CAT_IMG_DIR_.$id_category.'.jpg', _PS_CAT_IMG_DIR_.$id_category.'-'.stripslashes($image_type['name']).'.jpg', (int)$image_type['width'], (int)$image_type['height'] ); } } return $ret; }
Loại bỏ dòng cuối return $ret;
, thay vào đó thêm đoạn code cho ảnh thứ 2:
$ret2 = $this->uploadImage($id.'_second', 'image2', $this->fieldImageSettings['dir'].'/'); if (($id_category = (int)Tools::getValue('id_category')) && isset($_FILES) && count($_FILES) && $_FILES['image2']['name'] != null && file_exists(_PS_CAT_IMG_DIR_.$id_category.'_second.jpg')) { $images_types = ImageType::getImagesTypes('categories'); foreach ($images_types as $k => $image_type) { ImageManager::resize( _PS_CAT_IMG_DIR_.$id_category.'_second.jpg', _PS_CAT_IMG_DIR_.$id_category.'_second-'.stripslashes($image_type['name']).'.jpg', (int)$image_type['width'], (int)$image_type['height'] ); } } return $ret && $ret2;
Giải thích: đầu tiên chúng ta sao chép code ở adminController. Và đổi tên với trường upload ảnh, trong phần nhập category có id chứa hậu tố _second và tên trường là “image2”. Phần còn lại chỉ đơn giản copy/paste và đổi tên thành trường file khác. Cuối cùng sẽ trả về true nếu cả 2 trường upload đều có dữ liệu.
Có lỗi? thời điểm bắt đầu, Prestashop cho rằng chúng ta chỉ có một ảnh được sử dụng cho category. Vì vậy, khi upload mọi ảnh khác (loại trừ thumbs) “trường hợp này là trường thứ 2 chúng ta đang tạo nhé” thì nó sẽ xóa đi ảnh trước. Chúng ta có thể kiểm tra ngay được bằng cách upload ảnh mới cho trường ‘image2’, bạn sẽ nhận thấy ảnh mặc định của category sẽ bị xóa mất. Tuy nhiên bạn không muốn vậy, để fix lỗi này chúng ta kế thừa và sửa lại hàm imageUpload() trong file AdminController.php, chép nội dung của phương thức uploadImage vào controller của chúng ta:
Chú ý vào đoạn code sau đây:
// Delete old image if (Validate::isLoadedObject($object = $this->loadObject())) $object->deleteImage(); else return false;
Chính là nơi ảnh gốc bị xóa để thay thế ảnh khác đang upload tiếp, tránh làm nặng lưu trữ web. Sửa lại thành như sau:
// Delete old image if (Validate::isLoadedObject($object = $this->loadObject())) { if($name == 'image2') $object->deleteImage2(); else $object->deleteImage(); } else return false;
Đoạn code sẽ quyết định ảnh nào sẽ bị xóa với trường upload nào. Tiếp đến chúng ta cần mở rộng thêm hàm cuối cùng ‘postProcess()’. Copy hàm này từ AdminCategoriesController gốc trông giống thế này:
public function postProcess() { if (!in_array($this->display, array('edit', 'add'))) $this->multishop_context_group = false; if (Tools::isSubmit('forcedeleteImage') || (isset($_FILES['image']) && $_FILES['image']['size'] > 0) || Tools::getValue('deleteImage')) { $this->processForceDeleteImage(); if (Tools::isSubmit('forcedeleteImage')) Tools::redirectAdmin(self::$currentIndex.'&token='.Tools::getAdminTokenLite('AdminCategories').'&conf=7'); } return parent::postProcess(); }
Thêm vào đoạn code xóa ảnh thứ 2:
public function postProcess() { if (!in_array($this->display, array('edit', 'add'))) $this->multishop_context_group = false; if (Tools::isSubmit('forcedeleteImage') || (isset($_FILES['image']) && $_FILES['image']['size'] > 0) || Tools::getValue('deleteImage')) { $this->processForceDeleteImage(); if (Tools::isSubmit('forcedeleteImage')) Tools::redirectAdmin(self::$currentIndex.'&token='.Tools::getAdminTokenLite('AdminCategories').'&conf=7'); } else if(Tools::getValue('deleteImage2')) { $category = $this->loadObject(true); if (Validate::isLoadedObject($category)) if($category->deleteImage2(true)) Tools::redirectAdmin(self::$currentIndex.'&token='.Tools::getAdminTokenLite('AdminCategories').'&updatecategory&id_category='.$category->id.'&conf=7'); } return parent::postProcess(); }
Xong phần này, bước tiếp chúng ta mở rộng lớp Category.
Sửa đổi category class
Tạo file mới trong thư mục ‘override/classes’ và đổi tên thành Category.php. Trước hết chúng ta cần kiểm tra hoạt động trong Back office, bằng cách thêm phương thức deleteImage2().
Class Category extends CategoryCore { public function deleteImage2($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.'_second.'.$this->image_format) && !unlink($this->image_dir.$this->id.'_second.'.$this->image_format)) return false; } if (file_exists(_PS_TMP_IMG_DIR_.$this->def['table'].'_'.$this->id.'_second.'.$this->image_format) && !unlink(_PS_TMP_IMG_DIR_.$this->def['table'].'_'.$this->id.'_second.'.$this->image_format)) return false; if (file_exists(_PS_TMP_IMG_DIR_.$this->def['table'].'_mini_'.$this->id.'_second.'.$this->image_format) && !unlink(_PS_TMP_IMG_DIR_.$this->def['table'].'_mini_'.$this->id.'_second.'.$this->image_format)) return false; $types = ImageType::getImagesTypes(); foreach ($types as $image_type) if (file_exists($this->image_dir.$this->id.'_second-'.stripslashes($image_type['name']).'.'.$this->image_format) && !unlink($this->image_dir.$this->id.'_second-'.stripslashes($image_type['name']).'.'.$this->image_format)) return false; } return true; } }
Hàm này bạn cũng chỉ cần copy từ hàm deleteImage của objectModel
và sửa lại các tên để sử dụng hậu tố _second.
Tiến hành kiểm tra: truy cập vào tab categories, nhấn vào một trong số category bạn muốn thêm ảnh. Sau đó thử xóa ảnh để xem code hoạt động chính xác chưa. Rồi upload mới vào trường image2, nếu thành công ảnh gốc sẽ không bị xóa đi.
Tiếp đến, phần hiển thị ảnh mới. Chúng ta sẽ sử dụng trong trang subcategory, thông qua phương thức getSubcategories()
của lớp Category. Tiếp tục bạn Override phương thức này và dán vào lớp của chúng ta.
public function getSubCategories($id_lang, $active = true) { $sql_groups_where = ''; $sql_groups_join = ''; if (Group::isFeatureActive()) { $sql_groups_join = 'LEFT JOIN `'._DB_PREFIX_.'category_group` cg ON (cg.`id_category` = c.`id_category`)'; $groups = FrontController::getCurrentCustomerGroups(); $sql_groups_where = 'AND cg.`id_group` '.(count($groups) ? 'IN ('.implode(',', $groups).')' : '='.(int)Group::getCurrent()->id); } $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' SELECT c.*, cl.id_lang, cl.name, cl.description, cl.link_rewrite, cl.meta_title, cl.meta_keywords, cl.meta_description FROM `'._DB_PREFIX_.'category` c '.Shop::addSqlAssociation('category', 'c').' LEFT JOIN `'._DB_PREFIX_.'category_lang` cl ON (c.`id_category` = cl.`id_category` AND `id_lang` = '.(int)$id_lang.' '.Shop::addSqlRestrictionOnLang('cl').') '.$sql_groups_join.' WHERE `id_parent` = '.(int)$this->id.' '.($active ? 'AND `active` = 1' : '').' '.$sql_groups_where.' GROUP BY c.`id_category` ORDER BY `level_depth` ASC, category_shop.`position` ASC'); foreach ($result as &$row) { $row['legend'] = 'no picture'; } return $result; }
Sau dòng:
$row['id_image'] = Tools::file_exists_cache(_PS_CAT_IMG_DIR_.$row['id_category'].'.jpg') ? (int)$row['id_category'] : Language::getIsoById($id_lang).'-default';
Chén đoạn code cho ảnh mới:
$row['id_image2'] = Tools::file_exists_cache(_PS_CAT_IMG_DIR_.$row['id_category'].'_second.jpg') ? (int)$row['id_category'] .'_second' : Language::getIsoById($id_lang).'-default';
Xong !
Hiển thị ảnh trong template
Mở category.tpl trong folder theme. Tìm đến:
{if $subcategory.id_image} <img class="replace-2x" src="{$link->getCatImageLink($subcategory.link_rewrite, $subcategory.id_image, 'medium_default')|escape:'html':'UTF-8'}" alt="" width="{$mediumSize.width}" height="{$mediumSize.height}" /> {else}
Sửa thành:
{if $subcategory.id_image2} <img class="replace-2x" src="{$link->getCatImageLink($subcategory.link_rewrite, $subcategory.id_image2, 'medium_default')|escape:'html':'UTF-8'}" alt="" width="{$mediumSize.width}" height="{$mediumSize.height}" /> {else}
Nó sẽ hoạt động ngay nếu như không kích hoạt url rewrite. Nhưng nếu website đang dùng rewrite URL thì sao? sửa vào file .htaccess là cần thiết, tiếp tục thêm đoạn sau vào phía trên cùng, trước dòng:
# ~~start~~ Do not remove this comment, Prestashop will keep automatically the code outside this comment when .htaccess will be generated again
<IfModule mod_rewrite.c> RewriteRule ^c/([0-9]+)_second(\-[\.*_a-zA-Z0-9-]*)(-[0-9]+)?/.+\.jpg$ %{ENV:REWRITEBASE}img/c/$1_second$2$3.jpg [L] </IfModule>
Kết luận
Mặc dù có phức tạp nhưng tất cả đều là kế thừa và mở rộng tính năng cũ, việc thêm trường ảnh thứ hai cho category hay các trường khác cũng đều sử dụng cách này. Chúc bạn thành công!
Hãy cho mình biết suy nghĩ của bạn trong phần bình luận bên dưới bài viết này. Hãy theo dõi kênh chia sẻ kiến thức WordPress của Hoangweb trên Twitter và Facebook
- shares
- Facebook Messenger
- Gmail
- Viber
- Skype