jueves, 17 de abril de 2014

Creación de una entrada de datos con estilo usando Zend Framework (2a parte)

Nuestro paso inicial es crear el formulario de creación/edición.
Para ello añadiremos el fichero /application/forms/Doc.php:

<?php

class Form_Doc extends Zend_Form
{
    public function init ()
    {
        $this->setMethod('post');
        // Nou element id (hidden)
        $id = $this->createElement('hidden', 'id');
        // Opcions de l'element
        $id->setDecorators(array(
                'ViewHelper'
        ));
        $this->addElement($id);
        // Nou element Títol
        $titol = $this->createElement('text', 'titol')
            ->setLabel('Títol: ')
            ->setRequired(TRUE)
            ->setAttrib('size', 40)
            ->addFilter('StripTags');
        $this->addElement($titol);
        // Nou element descripció
        $descripcio = $this->createElement('text', 'descripcio')
            ->setLabel('Descripció: ')
            ->setRequired(FALSE)
            ->addFilter('StripTags');
        $this->addElement($descripcio);
        // Nou element Autors
        $autors = $this->createElement('multiselect', 'lst_autors')
            ->setLabel('Author: ')
            ->setRequired(TRUE)
            ->setAttrib('class', 'form-field')
            ->setRegisterInArrayValidator(false)
            ->setAttrib('multiple', 'multiple');
        $this->addElement($autors);
        // Nou element submit
        $submit = $this->createElement('submit', 'submit')->setLabel('Submit');
        $this->addElement($submit);
    }
}

En el modelo de datos será necesario incluir la función miembro para la creación del documento. Para ello editaremos el fichero /application/models/Docs.php y añadiremos las siguientes funciones:

    public function createDocs ($titol, $descripcio, $lst_autors)
    {
        // Creació d'un nou registre
        $row = $this->createRow();
        if ($row) {
            // actualitzem valors
            $row->titol = $titol;
            $row->descripcio = $descripcio;
            if ($row->save()) {
                // $id_document = $this->_db->lastInsertId();
                $id_document = $row->id_document;
                $this->salvaAutors($id_document, $lst_autors);
            }
            return true;
        } else {
            throw new Zend_Exception("El document no s'ha pogut crear!");
        }
    }

    private function salvaAutors ($id_document, $lst_autors)
    {
        $db = Zend_Db_Table::getDefaultAdapter();
        foreach ($lst_autors as $id_autor) {
            $data = array(
                    'id_document' => $id_document,
                    'id_autor' => $id_autor
            );
            $rows_affected = $db->insert('autors_documents', $data);
        }
    }

En el controlador añadiremos la acción create para que responda a la petición de creación de nuevo documento, para ello editaremos el fichero /application/controllers/DocsController.php y incluiremos la siguiente función miembro:
    public function createAction ()
    {
        $frm = new Form_Doc();
        if ($this->getRequest()->isPost()) {
            if ($frm->isValid($_POST)) {
                $titol = $frm->getValue('titol');
                $descripcio = $frm->getValue('descripcio');
                $lst_autors = $frm->getValue('lst_autors');
                $mdl = new Model_Docs();
                $result = $mdl->createDocs($titol, $descripcio, $lst_autors);
                if ($result) {
                    // redirecció a l'acció index
                    return $this->_redirect('/docs/index');
                }
            }
        }
        
        $frm->setAction('/docs/create');
        $this->view->form = $frm;
    }

Antes de finalizar hemos de crear una vista para la acción que acabamos de definir, para ello añadiremos el siguiente fichero /application/views/scripts/docs/create.phtml con el contenido:

<h2><?php echo $this->translate('Creació d\' un nou document'); ?></h2>
<p><?php echo $this->translate('Per crear un nou document complementi aquest formulari i polsi acceptar...'); ?></p>
<?php echo $this->form; ?>


Ahora ya podemos ejecutar nuestro formulario para ver que pinta tiene:


Como podemos observar el acabado del multiselección es bastante pobre. Vamos a incluir el plugin de jQuery, a ver que tal queda.
Primero de todo hemos de ubicar los scripts de Multiselect y jQuery en las carpetas adecuadas. Los ficheros deberían ser:
  1. /public/js/jquery.lwMultiSelect.js
  2. /public/js/jquery-1.11.0.js
  3. /public/css/jquery.lwMultiSelect.css
  4. /public/doublearrow.png
Las carpetas /public/js y /public/css deberán ser creadas y ubicar allí los scripts.
Modificaremos la vista de creación de documentos para que incluya los scripts e inicialice el multiselect. Editamos /application/views/scripts/docs/create.phtml:
    
<?php
$this->headScript()->prependFile('/js/jquery.lwMultiSelect.js');
$this->headScript()->prependFile('/js/jquery-1.11.0.js');
$this->headLink()->prependStylesheet('/css/jquery.lwMultiSelect.css');
$strAddAll = $this->translate('Afegir tots');
$strClear = $this->translate('Netejar');
$strSelected = $this->translate('Seleccionats');
$this->headScript()->appendScript('$(document).ready(function() {
$("#lst_autors").lwMultiSelect({
        addAllText:\'' . $strAddAll . '\',
        removeAllText:\'' . $strClear . '\', 
        selectedLabel:\'' . $strSelected . '\'});
});
', $type = 'text/javascript');
?>
<h2><?php echo $this->translate('Creació d\' un nou document'); ?></h2>
<p><?php echo $this->translate('Per crear un nou document complementi aquest formulari i polsi acceptar...'); ?></p>
<?php echo $this->form; ?>


Ahora nuestro formulario tendrá un aspecto mucho más profesional:

Creación de una entrada de datos con estilo usando Zend Framework (1a parte)

El objetivo es poder crear una entrada de datos para un formulario de forma agradable con Zend Framework, para ello crearemos la alimentación de datos de una tabla que contiene más de 1000 registros, y de cómo crear un formulario que nos permita trabajar con tantos datos de forma rápida y ágil.

Para editar un formulario con estilo usaremos un plugin de jQuery qne nos brinda la selección de múltiples opciones usando un select.
La url de descarga es: jQuery plugin Light Weight Multiselect

Consideraciones prévias:

Disponemos de dos tablas: autores y documentos con una relación de N a N entre ellas. La tabla de autores son muchos los registros de que dispone, y la tabla de documentos debe alimentarse de uno o varios autores.
La definición de tablas es:

CREATE TABLE `autors` (
  `id_autor` int(11) NOT NULL AUTO_INCREMENT,
  `descripcio` varchar(250) DEFAULT NULL,
  PRIMARY KEY (`id_autor`)
) ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS `documents` (
  `id_document` int(11) NOT NULL AUTO_INCREMENT,
  `titol` varchar(250) NOT NULL,
  `descripcio` varchar(250) NOT NULL,
  PRIMARY KEY (`id_document`)
) ENGINE=InnoDB;

La tabla que relaciona a ambas:
CREATE TABLE IF NOT EXISTS `autors_documents` (
  `id_document` int(11) NOT NULL DEFAULT '0',
  `id_autor` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id_document`,`id_autor`),
  KEY `id_document` (`id_document`),
  KEY `id_autor` (`id_autor`)
) ENGINE=InnoDB;

ALTER TABLE `autors_documents`
  ADD CONSTRAINT `autors_documents_ibfk_1` 
    FOREIGN KEY (`id_document`) REFERENCES `documents` (`id_document`),
  ADD CONSTRAINT `autors_documents_ibfk_2` 
    FOREIGN KEY (`id_autor`) REFERENCES `autors` (`id_autor`);

Para este ejemplo crearemos un proyecto nuevo en nuestra carpeta de trabajo al que llamaremos ZenSelect, en mi caso la carpeta de trabajo será "C:\Users\carles\Documents\workspaces", así pues en el fichero de configuración de apache incluiremos una directiva nueva para nuestro host:

<VirtualHost *:80>
    DocumentRoot "C:\Users\carles\Documents\workspaces\ZenSelect\public"
    ServerName zenselect.localhost
    SetEnv APPLICATION_ENV development
    <Directory "C:\Users\carles\Documents\workspaces\ZenSelect\public">
        Options Indexes MultiViews FollowSymLinks
        AllowOverride All
        Order allow,deny
        Allow from all
    </Directory>
</VirtualHost>

En nuestro fichero "C:\Windows\System32\drivers\etc\hosts" incluiremos el dominio de nuestro host para que sea accesible:
127.0.0.1 zenselect zenselect.localhost

Reiniciamos servicio web. Hecho esto podremos acceder a nuestro site ejecutando http://zenselect.localhost.

Configuraremos los parámetros de acceso a la base de datos en el fichero "/application/configs/application.ini":
Nota: Previamente hemos creado una base de datos con las credenciales de usuario zendselect y contraseña zendselect.

resources.view.encoding = "ISO-8859-1"
resources.db.adapter = PDO_MYSQL
resources.db.params.host = localhost
resources.db.params.username = zendselect
resources.db.params.password = zendselect
resources.db.params.dbname = zendselect

Incluiremos en nuestro fichero /application/bootstrap.php la función _initView y _initAutoload para definir el layout de salida e incluir los directorios de forms y models con los formularios y modelos correspondientes.
<?php

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{

    protected function _initView ()
    {
        $view = new Zend_View();
        $options = $this->getOptions();
        if (isset($options['resources']['view'])){
            $view = new Zend_View($options['resources']['view']);
        } else{
            $view = new Zend_View();
        }         
        $view->doctype("XHTML1_STRICT");
        $view->headMeta()->appendHttpEquiv("Content-Type", 
                "text/html;charset=ISO-8859-1");
        $view->headMeta()->appendHttpEquiv("Content-Language", "es_ES");
        $view->headTitle("Zend Select");
        $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper(
                'ViewRenderer');
        $viewRenderer->setView($view);
        return $view;
    }

    protected function _initAutoload ()
    {
        $autoLoader = Zend_Loader_Autoloader::getInstance();
        $autoLoader->registerNamespace('CMS_');
        $resourceLoader = new Zend_Loader_Autoloader_Resource(
                array(
                        'basePath' => APPLICATION_PATH,
                        'namespace' => '',
                        'resourceTypes' => array(
                                'form' => array(
                                        'path' => 'forms/',
                                        'namespace' => 'Form_'
                                ),
                                'model' => array(
                                        'path' => 'models/',
                                        'namespace' => 'Model_'
                                )
                        )
                ));
        // Return it so that it can be stored by the bootstrap
        return $autoLoader;
    }
}

Crearemos un conjunto de carpetas llamados /application/layout/scripts y allí ubicaremos nuestro fichero de layout layout.phtml, cuyo contenido será:
<?php
    echo $this->doctype();
?>
<html>
<head>
<?php
    echo $this->headMeta();
    echo $this->headTitle();
    echo $this->headScript();
    echo $this->headLink();
?>
</head>
<body>
    <?php echo $this->layout()->content; ?>
</body>
</html>

Hemos de incluir en nuestro fichero de configuración /application/configs/application.ini la definición del fichero de layout.

resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"

Para finalizar las consideraciones iniciales crearemos la carpeta "forms" dentro de "application".


Listado/creación/edición/borrado de Documents

Antes de crear el controlador que nos permita gestionar las acciones que sobre nuestros datos en "Documents", crearemos los modelos que nos permitan interaccionar sobre las tablas de "autors" y "documents".

Fichero /application/models/Autors.php:
<?php
require_once 'Zend/Db/Table/Abstract.php';

class Model_Autors extends Zend_Db_Table_Abstract {
    protected $_name = 'Autors';

    public function getAutors($filters = array(), $sortField = null) {
    $select = $this->select ();
    if ($sortField != null)
        $select->order ( $sortField );
    // afegirem els filtres passats
    if (count ( $filters ) > 0) {
        foreach ( $filters as $field => $filter ) {
            if ($field == 'descripcio ')
                $select->where ( $field . ' LIKE %?', $filter );
            else
                $select->where ( $field . ' = ?', $filter );
        }
    }
    // Afegim l'ordenació si cal
    $rows = $this->fetchAll ( $select );
    if ($rows->count () > 0) {
        return $rows;
    } else {
        return null;
    }
}

Fichero /application/models/Docs.php:
<?php
require_once 'Zend/Db/Table/Abstract.php';
class Model_Docs extends Zend_Db_Table_Abstract {
    protected $_name = 'Documents';

    public function getDocs()
    {
        $select = $this->select();
        $select->order(array('descripcio'));
        $rows = $this->fetchAll($select);
        if($rows->count() > 0) {
            return $rows;
        }else{
            return null;
        }
    }
}
El siguiente paso es crear el controlador de nuestros documentos:
Fichero /application/controllers/DocsController.php:
<?php
class DocsController extends Zend_Controller_Action
{

    public function init (){    } 

    public function indexAction ()
    {
        $mdl = new Model_Docs();
        $this->view->docs = $mdl->getDocs();
    }
}

Fichero /application/views/scripts/docs/index.phtml:

<h2><?php echo $this->translate('Current Documents'); ?></h2>
<?php if($this->docs != null) { ?>
<table class='spreadsheet' cellpadding='0' cellspacing='0'>
    <tr>
        <th>&nbsp;</th>
    <th><?php echo $this->translate('Títol Document'); ?></th>
    </tr>
<?php echo $this->partialLoop('partials/_docs-row.phtml', $this->docs); ?>
</table>
<?php }else{?>
<p><?php echo $this->translate('Encara no hi ha cap document definit.'); ?></p>
<?php }?>
<p>
    <a href='/docs/create'><?php echo $this->translate('Crear nou document'); ?></a>
</p>

Crearemos una carpeta en /application/views/scripts/partials, y dentro crearemos un nuevo Fichero /application/views/scripts/partials/_docs-row.phtml:
 
<tr>
 <td class='links'><a href='/docs/edit/id/<?php echo $this->id_document;?>'><?php echo $this->translate('Editar'); ?></a>
  | <a href='/docs/delete/id/<?php echo $this->id_document;?>'><?php echo $this->translate('Esborrar'); ?></a>
 </td>
 <td><?php echo $this->descripcio ?></td>
</tr>

Para finalizar incluiremos en nuestro archivo /application/configs/application.ini el módulo por defecto de visualización que sea nuestro docs:

resources.frontController.defaultControllerName = "docs"

De esta forma ya tenemos el esqueleto incial de nuestro proyecto.
En el siguiente capítulo veremos como crear un formulario de edición con el código jQuery Multiselect.