En el ejemplo anterior podíamos de una forma bastante sencilla hacer el “cambio” de una caja de texto a un selector mediante el uso de jquery.
El objetivo ahora seria añadir en la sección de personalización sus correspondientes campos de forma homóloga a los campos de texto para gestionar la aparición de selectores en el front end de la tienda.
No soy un experto en Prestashop y considero que la mejor forma de aprender las tripas de un programa es precisamente metiéndose manos a la obra. Primero de todo resaltar que éste ejemplo esta realizado en la versión 1.5.4 de Prestashop.
A nivel de documentación la mejor fuente, como no debería ser de otra manera, se encuentra en la página oficial de Prestashop y en sus foros. Ahí explica como actuar sobre los controladores (overload por herencia) y las plantillas para el back office. Para ello hay que seguir una estructura de nombres determinada bajo la carpeta override. En la siguiente imagen se ve la estructura de carpetas y resaltados los ficheros que nos atañen ahora.
El controlador que hay que modificar es AdminProductsControllerCore
y la plantilla es customization.tpl
. También hay que modificar la clase ProductCore
que se ocupa del modelo de datos y es la que hace las consultas y inserciones en base de datos.
Cambios en base de datos
Afortunadamente el cambio que introducimos es bastante simétrico y nos podemos enganchar a como prestashop gestiona la personalización de los campos de texto. Enseguida se ve que necesitamos un campo nuevo en el modelo de datos para productos. El nuevo campo lo llamamos $select_fields
y contiene el número de selectores que queremos para la personalización de ese producto. Añadimos este campo select_fields
en la tabla ps_products
junto a su campo “hermano” text_fields
.
Así tenemos que nuestra nueva clase Product
hereda del core de Prestashop
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class Product extends ProductCore { /** @var int Number of select fields */ public $select_fields; const CUSTOMIZE_SELECTFIELD = 2; public function __construct($id_product = null, $full = false, $id_lang = null, $id_shop = null, Context $context = null) { //'select_fields' => array('type' => self::TYPE_INT, 'shop' => true, 'validate' => 'isUnsignedInt'), self::$definition['fields']['select_fields'] = array('type' => self::TYPE_INT,'shop' => true, 'validate' => 'isUnsignedInt'); parent::__construct($id_product,$full,$id_lang,$id_shop,$context); } |
Para no sobrescribir la definición entera, en el constructor añadimos nuestra entrada donde definimos nuestro nuevo campo. Al ser un campo multitienda habrá que añadir también este campo a la tabla ps_products_shop
(ver doc). A nuestro nuevo tipo de customización le asignamos el número 2 ( 0 para el campo archivo, 1 para el campo texto).
Las etiquetas de estos campos de personalización se guardan en la tabla ps_customization_field
(y su correspondiente ps_customization_field_lang
para las traducciones) junto al tipo de campo y producto al que pertenecen. No hay que hacer ningún cambio, simplemente nuestras nuevas etiquetas se incluirán bajo el tipo 2.
Ahora hay que tener en cuenta que nuestro nuevo campo, a parte de tener una etiqueta como los otros dos, necesita guardar la información para construir luego el selector. Lo mas apropiado es crear una nueva tabla (o tablas). Para simplificar creamos una única tabla ps_customization_select_field_lang
que guardará la lista del selector en los diferentes idiomas junto con el identificador de su campo personalizado de la tabla ps_customizadion_field
.
Presentación del contenido en pantalla
La función que se ocupa de generar el contenido para la plantilla dentro de la clase AdminProductsControllerCore
es initFormCustomization
. Ésta será la primera que sobrescribamos en nuestra nueva clase hija AdminProductsController
. Se copia y pega la función padre para luego añadir las modificaciones
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
class AdminProductsController extends AdminProductsControllerCore { public function initFormCustomization($obj) { $data = $this->createTemplate($this->tpl_form); if ((bool)$obj->id) { if ($this->product_exists_in_shop) { //El obj es el producto $labels = $obj->getCustomizationFields(); $has_file_labels = (int)$this->getFieldValue($obj, 'uploadable_files'); $has_text_labels = (int)$this->getFieldValue($obj, 'text_fields'); $has_select_labels = (int)$this->getFieldValue($obj, 'select_fields'); $data->assign(array( 'obj' => $obj, 'table' => $this->table, 'languages' => $this->_languages, 'has_file_labels' => $has_file_labels, 'display_file_labels' => $this->_displayLabelFields($obj, $labels, $this->_languages, Configuration::get('PS_LANG_DEFAULT'), Product::CUSTOMIZE_FILE), 'has_text_labels' => $has_text_labels, 'display_text_labels' => $this->_displayLabelFields($obj, $labels, $this->_languages, Configuration::get('PS_LANG_DEFAULT'), Product::CUSTOMIZE_TEXTFIELD), 'uploadable_files' => (int)($this->getFieldValue($obj, 'uploadable_files') ? (int)$this->getFieldValue($obj, 'uploadable_files') : '0'), 'text_fields' => (int)($this->getFieldValue($obj, 'text_fields') ? (int)$this->getFieldValue($obj, 'text_fields') : '0'), 'has_select_labels'=>$has_select_labels, 'select_fields'=> (int)($this->getFieldValue($obj, 'select_fields') ? (int)$this->getFieldValue($obj, 'select_fields') : '0'), 'display_select_labels' => $this->_displayLabelFields($obj, $labels, $this->_languages, Configuration::get('PS_LANG_DEFAULT'), Product::CUSTOMIZE_SELECTFIELD), )); } else $this->displayWarning($this->l('You must save the product in this shop before adding customization.')); } else $this->displayWarning($this->l('You must save this product before adding customization.')); $this->tpl_form_vars['custom_form'] = $data->fetch(); } |
A la plantilla hay que pasarle las nuevas variables para que pueda presentar el nuevo campo. De forma homóloga a los otros dos campos estas variables son select_fields
, has_select_labels
y display_select_labels
. El primero es el número de campos selectores, el segundo sirve para saber si hay alguno y el tercero es el mas complejo ya que incluye todo el código html para la presentación de estos campos selectores. Ésta última es la que nos hace tirar del hilo y sobrescribir unas cuantas funciones más tanto en el controlador como en la clase Product.
Los cambios en la plantilla customization.tpl
son muy simples. Se añade el nuevo campo con el número de selectores después de campos de texto:
1 2 3 4 5 6 7 8 9 10 |
<tr> <td style="width:150px;text-align:right;padding-right:10px;font-weight:bold;vertical-align:top;" valign="top"> {include file="controllers/products/multishop/checkbox.tpl" field="text_fields" type="default"} <label>{l s='Select fields:'}</label> </td> <td style="padding-bottom:5px;"> <input type="text" name="select_fields" id="select_fields" size="4" value="{$select_fields|htmlentities}" /> <p class="preference_description">{l s='Number of select fields displayed'}</p> </td> </tr> |
y luego en un if se muestran uno por uno los selectores que se encuentran en la variable $display_select_labels
:
1 2 3 4 5 6 7 8 9 10 11 |
{if $has_select_labels} <tr> <td colspan="2"><div class="separation"></div></td> </tr> <tr> <td style="width:200px" valign="top">{l s='Define the label of the select fields:'}</td> <td> {$display_select_labels} </td> </tr> {/if} |
text_fields
, la mayoría de veces se trata de copiar pegar y cambiar, aunque evidentemente hay que tener en cuenta nuestra nueva tabla donde se guarda el selector.Procesar el guardado de las personalizaciones
La función que se ocupa de guardar los cambios en el controlador es processCustomizationConfiguration
. Al igual que antes se copia y pega la función padre para ir realizando las modificaciones. Esta quedaría una vez modificada de la siguiente forma:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
/* * Se llama al guardar el tab de Personalización */ public function processCustomizationConfiguration() { $product = $this->object; // Get the number of existing customization fields ($product->text_fields is the updated value, not the existing value) $current_customization = $product->getCustomizationFieldIds(); $files_count = 0; $text_count = 0; $select_count= 0; if (is_array($current_customization)) { foreach ($current_customization as $field) { if ($field['type'] == 1) $text_count++; elseif ($field['type'] == 2) $select_count++; else $files_count++; } } if (!$product->createLabelsAndCombos((int)$product->uploadable_files - $files_count, (int)$product->text_fields - $text_count, (int)$product->select_fields - $select_count)) $this->errors[] = Tools::displayError('An error occurred while creating customization fields.'); if (!count($this->errors) && !$product->updateLabelsAndCombos()) $this->errors[] = Tools::displayError('An error occurred while updating customization fields.'); $product->customizable = ($product->uploadable_files > 0 || $product->text_fields > 0 || $product->select_fields > 0) ? 1 : 0; if (!count($this->errors) && !$product->update()) $this->errors[] = Tools::displayError('An error occurred while updating the custom configuration.'); } |
Como antes, esta función es de la que partimos para hacer luego mas cambios. En este caso habrá que modificar funciones de la clase Product
, p.ej lo que antes era createLabels
ahora será createLabelsAndCombos
.
Por último para un tema de presentación de las banderitas multiidioma nos interesa esconder las que aparecen en los nuevas áreas de texto donde introduciremos las listas. Para añadir nuestro css (myproductadmin.css
) podemos sobrescribir la función setMedia
:
1 2 3 4 5 |
public function setMedia(){ parent::setMedia(); //añadimos nuestro css $this->addCSS(__PS_BASE_URI__.'override/controllers/admin/css/myproductadmin.css', 'all'); } |
Resumen
A nivel de base de datos hemos añadido un nuevo campo a las tablas ps_product
y ps_product_shop
, hemos creado una tabla ps_customization_select_field_lang
para guardar los selectores en los distintos idiomas.
A nivel de código hemos necesitado heredar de ProductCore
y AdminProductsControllerCore
para sobrescribir un buen puñado de métodos. Para la presentación hemos copiado y añadido código a customization.tpl
y hemos creado un css (myproductadmin.css
) para utilizar nuestros estilos.
Hemos conseguido gestionar en el back office un nuevo campo selector para que luego podamos utilizarlo en el front office.