Issues https://www.carloscarrascal.com/ es Acceso a base datos en Drupal 7. Parte 2 https://www.carloscarrascal.com/blog/acceso-base-datos-en-drupal-7-parte-2 <article data-history-node-id="37" class="node node--type-blog-post node--view-mode-rss group-one-column ds-2col-stacked-fluid clearfix"> <div class="group-header"> <div class="field field--name-node-title field--type-ds field--label-hidden field--item"><h1> Acceso a base datos en Drupal 7. Parte 2 </h1> </div> <div class="field field--name-node-post-date field--type-ds field--label-hidden field--item">Viernes, Noviembre 17, 2017 - 22:26</div> </div> <div class="group-left"> <div class="field field--name-field-tags field--type-entity-reference field--label-hidden field--items"> <div class="field--item"><a href="/tags/drupal" hreflang="es">Drupal</a></div> <div class="field--item"><a href="/tags/d7" hreflang="es">D7</a></div> <div class="field--item"><a href="/tags/database" hreflang="es">Database</a></div> <div class="field--item"><a href="/tags/issues" hreflang="es">Issues</a></div> </div> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p>Continuamos con el ejemplo de acceso a base de datos en Drupal 7 que comenzamos hace unos días. Puedes consultar <a href="/blog/acceso-base-datos-en-drupal-7">la primera parte del artículo</a> para refrescar o si simplemente te la perdiste.</p> <p>En esta segunda parte vamos a ver cómo:</p> <ul> <li>Crear un formulario para que el usuario pueda crear un nuevo registro en nuestra tabla de <em>issues</em>.</li> <li>Usar el mismo formulario para modificar los datos de una incidencia.</li> <li>Eliminar el registro de una incidencia de la tabla.</li> </ul> <p> </p> <h2>Definir Hook menus para las acciones</h2> <p>Lo primero que vamos a hacer es ampliar nuestra implementación de <em>hook_menu()</em> para declarar las tres URLs nuevas que necesitamos:</p> <ul> <li>Una URL para modificar los datos de una incidencia: <em>/issues/edit/[issue_id]</em></li> <li>Otra para eliminar incidencias de la tabla: <em>/issues/delete/[issue_id]</em> </li> <li>Por último, para dar de alta incidencias desde un formulario de Drupal:  <em>/issues/create</em></li> </ul> <p>Atención, porque las dos primeras van a necesitar un parámetro que será el identificador (<em>issue_id</em>) de la incidencia que queremos modificar o eliminar, y que debemos especificar en el <em>hook_menu()</em>, que quedaría así (ojo a las partes en negrita):</p> <pre> /** * Implements hook_menu(). * * Defines URLS for the list, create, edit and delete actions. */ function issue_control_menu() { $items = array(); $items['issues/list'] = array( 'title' =&gt; 'Issues List', 'description' =&gt; 'Issues List from the database.', 'page callback' =&gt; 'issue_control_list', 'page arguments' =&gt; array(), 'access callback' =&gt; TRUE, ); $items['issues/create'] = array( 'title' =&gt; 'Issues Form', 'description' =&gt; 'A form to write rows into issues table.', 'page callback' =&gt; 'drupal_get_form', 'page arguments' =&gt; array('issue_control_form'), 'access callback' =&gt; TRUE, ); $items['<strong>issues/edit/%</strong>'] = array( 'title' =&gt; 'Issues Form', 'description' =&gt; 'A form to write rows into issues table.', 'page callback' =&gt; 'drupal_get_form', '<strong>page arguments</strong>' =&gt; <strong>array('issue_control_form', 2)</strong>, 'access callback' =&gt; TRUE, ); $items['<strong>issues/delete/%</strong>'] = array( 'title' =&gt; 'Delete issue', 'description' =&gt; 'Deletes an issue.', 'page callback' =&gt; 'issue_control_perform_delete', '<strong>page arguments</strong>' =&gt; <strong>array(2)</strong>, 'access callback' =&gt; TRUE, ); return $items; } </pre> <p>Las definiciones de las URLs de modificar y eliminar llevan un símbolo <em><strong>%</strong></em> que actua de comodín, y se pasa después como parámetro a la función que definimos en <em>'page callback'</em>, mediante la siguiente línea de <em>'page arguments'</em>. En nuestro caso estamos pasando el argumento <strong>2</strong>, ya que el 0 sería 'issues', el 1 sería 'delete' o 'edit', y el 2 será el identificador de la incidencia que ya estamos incluyendo en la URL cuando creamos nuestra lista.</p> <p>De esta forma, cuando llamamos a la url <em><a href="http://nuestro.drupal/issue/edit/1">http://nuestro.drupal/issue/edit/1</a></em>, el último 1 (que es el <em>issue_id</em>) se recibirá en nuestra función como un parámetro.</p> <p>Además, en el caso de las llamadas de modificar y crear, la función que ponemos en el '<em>page callback</em>' no es directamente una función declarada por nosotros, sino una llamada a <a href="https://api.drupal.org/api/drupal/includes%21form.inc/function/drupal_get_form/7.x">drupal_get_form()</a>, a la que pasamos el nombre de nuestra función que pintará realmente el formulario. </p> <p> </p> <h2>Acceso a datos: Las funciones básicas</h2> <p>Antes de empezar a desarrollar el formulario para editar y crear incidencias, vamos a empezar por la parte básica primero: las funciones de acceso a datos. Es lógico que implementemos estas funciones esenciales, ya que lo normal en aplicaciones grandes es que tengamos varias llamadas, por ejemplo, para cargar una incidencia en varias partes del código, por lo podremos reutilizalas, y favorecemos la encapsulación, el principio de responsabilidad simple, etc. </p> <p>Estas son las funciones que vamos a necesitar:</p> <table class="table table-striped"> <tbody> <tr> <th>Acción</th> <th>Función</th> <th>Descripción</th> </tr> <tr> <td>Cargar</td> <td>issue_control_load_issue()</td> <td>Devuelve una incidencia.</td> </tr> <tr> <td>Nueva</td> <td>issue_control_add()</td> <td>Crea una indicencia en la tabla.</td> </tr> <tr> <td>Modificar</td> <td>issue_control_edit()</td> <td>Modifica los datos de una incidencia.</td> </tr> <tr> <td>Eliminar</td> <td>issue_control_delete()</td> <td>Eliminar una indicencia.</td> </tr> </tbody> </table> <p>Es buena práctica en Drupal que todas los nombres de funciones que declaremos se precedan con el nombre de nuestro módulo. Asi nos evitaremos sorpresas desagradables, ya que si dos funciones en distintos módulos se llaman igual, tendremos un error de PHP.</p> <p> </p> <h3>Cargar incidencia</h3> <p>La primera función de acceso a datos que necesitamos va a ser para obtener los datos de una incidencia de la tabla a partir de su identificador (<em>issue_id</em>).</p> <pre> function issue_control_load_issue($issue_id) { $issue = NULL; if (isset($issue_id)) { $issue = db_select('{issues}', 'i') -&gt;fields('i') <strong> -&gt;condition('issue_id', $issue_id, '=') </strong> -&gt;execute() -&gt;fetchAssoc(); } return $issue; }</pre> <p>Estamos utilzando en este caso la función <a href="https://api.drupal.org/api/drupal/includes%21database%21database.inc/function/db_select/7.x">db_select()</a> de la API de Drupal para recuperar un único registro mediante la condición de que coincida el <em>issue_id</em> con el que recibimos por parámetro. Esta función nos devolverá un objeto de tipo <a href="https://api.drupal.org/api/drupal/includes%21database%21select.inc/class/SelectQuery/7.x" title="Query builder for SELECT statements.">SelectQuery</a> que este caso estamos ejecutando directamente en la misma línea con <em>execute()</em> y recuperando el registro con <em>fetchAssoc()</em>.</p> <p>La función nos devolverá un array con los valores de los campos hemos especifiquedo en <em>fields</em>, en este caso todos. Para acceder mas tarde a los datos de los campos lo haremos así:</p> <pre> $issue = issue_control_load_issue($issue_id); print ($issue['title']); </pre> <h3> </h3> <h3>Crear incidencia</h3> <p>La siguiente función que vamos a necesitar será para dar de alta una nueva incidencia en la tabla. El código es el siguiente:</p> <pre> /** * Creates an issue. */ function issue_control_add($form_state) { global $user; <strong> $issue_id = db_insert('{issues}') -&gt;fields(array( 'title' =&gt; $form_state['values']['title'], 'description' =&gt; $form_state['values']['description'], 'uid' =&gt; $user-&gt;uid, )) -&gt;execute();</strong> $message = t('New issue [@issue_id] has been created by uid [@uid].', array('@issue_id' =&gt; $issue_id, '@uid' =&gt; $user-&gt;uid) ); watchdog('watchdog_form', $message); drupal_set_message($message); } </pre> <p>En esta función estamos recibiendo como parámetro un array <em>$form_state</em>, que es que nos llega cuando se envía el formulario que construiremos mas tarde. De momento podeis ver que este <em>$form_state </em>obtenemos los valores de los campos para guardar el registro.</p> <p>El otro dato que necesitamos a la hora de crear la incidencia es el identificador del usuario que la esta creando. Para ello usamos la variable global <em>$user</em> de Drupal, de la que podemos obtenemos el <em>uid</em>.</p> <p>Aquí la parte importante es la función <a href="https://api.drupal.org/api/drupal/includes%21database%21database.inc/function/db_insert/7.x">db_insert()</a> de Drupal que es la encargada de guardar dicho registro. La sintaxis es muy sencilla, recibe por parámetro el nombre de la tabla y después usamos el método <em>fields</em> para añadir los campos y sus valores, y llamamos al <em>execute()</em> para lanzar la consulta.</p> <p>A continuación estamos creando un mensaje con información del registro que acabamos de crear con el que vamos a hacer dos cosas:</p> <ul> <li>Lo guardamos al <em>watchdog</em> de Drupal, para dejar un registro de cuando se creó.</li> <li>Lo pasamos a <em>drupal_set_message()</em>, y Drupal lo mostrará en la siguiente página, para informar al usuario.</li> </ul> <p>Esta función no devuelve ningún valor, aunque deberíamos añadir al menos un control de errores.</p> <h3> </h3> <h3>Modificar incidencia</h3> <p>Vamo ahora con la modificación de datos de una incidencia. En este caso también vamos a recibir por parámetro un array <em>$form_state</em> que vendrá del envío del formulario de edición.</p> <pre> function issue_control_edit($form_state) { global $user; db_update('{issues}') -&gt;fields(array( 'issue_id' =&gt; $form_state['values']['issue_id'], 'title' =&gt; $form_state['values']['title'], 'description' =&gt; $form_state['values']['description'], 'uid' =&gt; $user-&gt;uid, )) -&gt;condition('issue_id', $form_state['values']['issue_id'], '=') -&gt;execute(); $message = t('Issue [@issue_id] has been edited by uid [@uid].', array('@issue_id' =&gt; $form_state['values']['issue_id'], '@uid' =&gt; $user-&gt;uid) ); watchdog('watchdog_form', $message); drupal_set_message($message); }</pre> <p>Igual que en el caso de la creación del registro, usamos la variable global <em>$user</em> para obtener el uid del usuario que esta modificando la incidencia.</p> <p>Para este caso, utlizamos la función <a href="https://api.drupal.org/api/drupal/includes%21database%21database.inc/function/db_update/7.x">db_update()</a> de Drupal, y de forma similar a la creación del registro, obtenemos los valores de los campos del array <em>$form_state</em>, y establecemos que la condición para modificar los registros es la coincidencia por el issue_id con el que hemos recibido.</p> <p>De la misma forma que en el caso anterior, componemos un mensaje que se manda al watchdog y se muestra al usuario.</p> <p> </p> <h3>Eliminar incidencia</h3> <p>La última función de acceso a datos es para borrar incidencias de la tabla.</p> <pre> function issue_control_delete($issue_id) { global $user; db_delete('{issues}') -&gt;condition('issue_id', $issue_id) -&gt;execute(); $message = t('Issue [@issue_id] has been deleted by uid [@uid].', array('@issue_id' =&gt; $issue_id, '@uid' =&gt; $user-&gt;uid) ); watchdog('watchdog_form', $message); drupal_set_message($message); }</pre> <p>En este caso también es necesario recibir como parámetro el identificador de la incidencia para poder eliminarla utilizando <a href="https://api.drupal.org/api/drupal/includes%21database%21database.inc/function/db_delete/7.x">db_delete()</a> de Drupal.</p> <p>Como en los casos anteriores, componemos un mensaje informativo y lo enviamos al watchdog y al usuario.</p> <p> </p> <h2>El formulario de creación y modificación</h2> <p>Una vez tenemos nuestra API de acceso a base de datos, solo nos queda contruir el formulario de creación y modificación de incidencias, y la llamada para eliminarlas.</p> <p>Para construir el formulario vamos a utilizar la Form API de Drupal, que nos permite definir un formulario completo utilizando un array asociativo para definir los controles.</p> <p>La función se llamara <em>issue_control_form()</em> y quedará así, atención a las partes en negrita:</p> <pre> function issue_control_form($form, &amp;$form_status, <strong>$issue_id = NULL</strong>) { <strong>$issue = issue_control_load_issue($issue_id); </strong> $form['issue_id'] = array( '#type' =&gt; '<strong>textfield</strong>', '#title' =&gt; 'Id', '#required' =&gt; TRUE, <strong>'#disabled' =&gt; TRUE,</strong> '#default_value' =&gt; isset($issue) ? <strong>$issue['issue_id']</strong> : '', ); $form['title'] = array( '#type' =&gt; '<strong>textfield</strong>', '#title' =&gt; 'Title', '#required' =&gt; TRUE, '#default_value' =&gt; isset($issue) ? <strong>$issue['title']</strong> : '', ); $form['description'] = array( '#type' =&gt; '<strong>textarea</strong>', '#title' =&gt; 'Description', '#default_value' =&gt; isset($issue) ? <strong>$issue['description']</strong> : '', ); if (isset($issue)) { $submit_message = t('Save issue'); } else { $submit_message = t('Create issue'); } $form['submit_button'] = array( '#type' =&gt; '<strong>submit</strong>', '#value' =&gt; <strong>$submit_message</strong>, ); return $form; }</pre> <p>El formulario en este caso es muy sencillo, con tres campos y botón para envíar el formulario:</p> <ul> <li>Identificador <em>issue_id</em>: Será un campo de texto sencillo (<em>textfield</em>), pero lo vamos a desactivar para que el usuario no pueda escribir en el.</li> <li>Campo <em>title</em>: el título de la incidencia. Otro campo tipo <em>textfield</em>.</li> <li>Campo description: Descripción de la incidencia. Este campo es mas largo, asi que usaremos un tipo textarea para mostrar una caja de texto grande con multiples líneas. Este tipo de campo puede configurarse también para usar un editor de texto enriquecido.</li> </ul> <p>Usando Bootstrap se vería algo parecido a esto:</p> <div alt="Formulario de incidencias" data-embed-button="file_browser" data-entity-embed-display="image:image" data-entity-embed-display-settings="full_width_shadow" data-entity-type="file" data-entity-uuid="45c3a44d-280c-4355-a0df-fe52fac08248" title="Formulario de incidencias" data-langcode="es" class="embedded-entity"> <img src="/sites/default/files/styles/full_width_shadow/public/formulario_de_incidencias_0.png?itok=OG9QfK2S" alt="Formulario de incidencias" title="Formulario de incidencias" typeof="foaf:Image" class="img-responsive" /> </div> <p>Lo imporante en este caso es que esta función sera utilizada por <em>drupal_get_form()</em>, y puede recibir un tercer parámetro que será el <em>issue_id</em>, en el caso de que sea llamada desde el hook_menu() para modificar. </p> <p>En el caso de recibir ese parámetro, cargamos los datos de esa incidencia mediante <em>issue_control_load_issue()</em>, y ponemos los valores por defecto de los campos a los valores correspondientes, para que el usuario ya vea el formulario relleno con los datos y pueda modificarlos.</p> <p>Dependiendo del caso, ajustaremos tambien el texto del botón de enviar el formulario, mostrando el mensaje correspondiente a cada caso: Guardar o modificar.</p> <p>Ahora necesitamos la función que va a controlar que hacer cuando el usuario envía el formulario:</p> <pre> function issue_control_form_submit($form, &amp;$form_state) { if (empty($form_state['values']['issue_id'])) { issue_control_add($form_state); } else { issue_control_edit($form_state); } drupal_goto('/issues/list'); }</pre> <p>Esta función simplemente comprueba el <em>issue_id</em> que viene al envíar el formulario (en el <em>$form_state</em>) está vacío, en cuyo caso se tratará de una nueva incidencia, y llamamos a nuestra función <em>issue_control_add</em> para crear el nuevo registro.</p> <p>Si por el contrario tenemos un valor en <em>issue_id</em>, se tratará de una moficiación, por lo que se llama a <em>issue_control_edit</em>().</p> <p>En ambos casos, el usuario será redirigido a la página del listado de incidencias.</p> <h2>Eliminando incidencias</h2> <p>Para este ejemplo lo haremos muy sencillo, implementando la función que se llamará desde el <em>hook_menu</em>:</p> <pre> function issue_control_perform_delete($issue_id) { issue_control_delete($issue_id); drupal_goto('/issues/list'); } </pre> <p>Esta función simplemente llama a nuestra función de acceso a datos issue_control_delete() y le pasa el identificador de la incidencia. Una vez eliminada, devolvemos al usuario a la lista de incidencias.</p> <p>Lo suyo sería implementar algún mecanismo para obtener una confirmación del usuario antes de eliminar, para evitar errores, pero no me quiero extender mas con este ejercicio, mejor lo dejamos para otro rato. Si alguien tiene interés que me deje un comentario.</p> <p> </p> <p>Espero que el ejemplo no haya quedado demasiado espeso y que os ayude. A modo de cierre un par de cosas que quedan en el aire:</p> <ul> <li>Habria que sacar el HTML de la lista de incidencias que vimos en la primera parte a un template.</li> <li>Ojo porque no hemos hablado nada de seguridad. Todas las funciones de base de datos de Drupal que estamos usando estan protegidas contra inyección de SQL, pero es una buena práctica utilizar la funcion <a href="https://api.drupal.org/api/drupal/includes%21bootstrap.inc/function/check_plain/7.x">check_plain()</a> cada vez que vayamos a pintar información que viene de la base de datos.</li> </ul> <p>Muchas gracias y saludos</p> <p> </p> </div> </div> <div class="group-footer"> <section> <h2>Comments</h2> <article data-comment-user-id="0" id="comment-193" about="/en/comment/193" typeof="schema:Comment" class="js-comment"> <mark class="hidden" data-comment-timestamp="1619643785"></mark> <footer> <article typeof="schema:Person" about="/user/0"> </article> <p><span rel="schema:author">Subido por <span lang="" typeof="schema:Person" property="schema:name" datatype="">Milton Vintimilla (no verificado)</span> el Mié, 13/11/2019 - 03:01</span> <span property="schema:dateCreated" content="2019-11-13T02:01:55+00:00" class="hidden"></span> </p> <a href="/en/comment/193#comment-193" hreflang="en">Enlace permanente</a> </footer> <div> <h3 property="schema:name" datatype=""><a href="/en/comment/193#comment-193" class="permalink" rel="bookmark" hreflang="en">Consulta abrir venta de un link de una consulta base de datos</a></h3> <div property="schema:text" class="field field--name-comment-body field--type-text-long field--label-hidden field--item"><p>Muy interesante y completo sus explicación sobre las base de datos, me queda una inquietud, si me podría ayudar al listar los datos de una base datos y crear un link que abra una venta nueva y pida datos para una nueva consulta.<br /> Gracias.</p> </div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=193&amp;1=default&amp;2=en&amp;3=" token="vFFEYqK7Sd5IpVkr_-UNEvYZgzZqj38uw4l2Eq-yA4Y"></drupal-render-placeholder> </div> </article> <article data-comment-user-id="0" id="comment-778" about="/en/comment/778" typeof="schema:Comment" class="js-comment"> <mark class="hidden" data-comment-timestamp="1619644398"></mark> <footer> <article typeof="schema:Person" about="/user/0"> </article> <p><span rel="schema:author">Subido por <span lang="" typeof="schema:Person" property="schema:name" datatype="">javier Orti (no verificado)</span> el Lun, 04/01/2021 - 21:02</span> <span property="schema:dateCreated" content="2021-01-04T20:02:09+00:00" class="hidden"></span> </p> <a href="/en/comment/778#comment-778" hreflang="en">Enlace permanente</a> </footer> <div> <h3 property="schema:name" datatype=""><a href="/en/comment/778#comment-778" class="permalink" rel="bookmark" hreflang="en">tutorial</a></h3> <div property="schema:text" class="field field--name-comment-body field--type-text-long field--label-hidden field--item"><p>Esplendido!! me ayudado muchísimo, estoy siguiendo un tutorial sobre un libro de drupal 7 y en esta parte de entidades me había quedado clavado. Gracias a este tutorial me he quedado sin ninguna duda.Muchas gracias.</p> </div> <drupal-render-placeholder callback="comment.lazy_builders:renderLinks" arguments="0=778&amp;1=default&amp;2=en&amp;3=" token="TZpZIjrvaE8dVujgUkiBxOApPYOxt7mgKresV6AYcbQ"></drupal-render-placeholder> </div> </article> <h2>Añadir nuevo comentario</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=37&amp;2=comment&amp;3=comment" token="gvF5wqRkvRYszrMOWAxHDjMIL25UoMLzXWdz7viFYPw"></drupal-render-placeholder> </section> </div> </article> Fri, 17 Nov 2017 21:26:38 +0000 root 37 at https://www.carloscarrascal.com Acceso a base datos en Drupal 7 https://www.carloscarrascal.com/blog/acceso-base-datos-en-drupal-7 <article data-history-node-id="36" class="node node--type-blog-post node--view-mode-rss group-one-column ds-2col-stacked-fluid clearfix"> <div class="group-header"> <div class="field field--name-node-title field--type-ds field--label-hidden field--item"><h1> Acceso a base datos en Drupal 7 </h1> </div> <div class="field field--name-node-post-date field--type-ds field--label-hidden field--item">Sábado, Noviembre 11, 2017 - 14:50</div> </div> <div class="group-left"> <div class="field field--name-field-tags field--type-entity-reference field--label-hidden field--items"> <div class="field--item"><a href="/tags/d7" hreflang="es">D7</a></div> <div class="field--item"><a href="/tags/drupal" hreflang="es">Drupal</a></div> <div class="field--item"><a href="/tags/database" hreflang="es">Database</a></div> <div class="field--item"><a href="/tags/issues" hreflang="es">Issues</a></div> </div> <div class="field field--name-body field--type-text-with-summary field--label-hidden field--item"><p>Estamos haciendo unas sesiones de formación en el trabajo con Drupal 7, y nos ha quedado un ejercicio bastante completo que creo que merece la pena publicar por si puede ayudar a alguien mas.</p> <p>La idea es mostrar cómo podemos crear un módulo en Drupal 7 que cree su propia tabla en la base de datos, y realizar operaciones sobre ella usando la API de acceso a datos de Drupal.</p> <p>Para este ejemplo vamos a crear un módulo que nos permita gestionar una tabla sencilla de incidencias (<em>Issues</em>). Los objetivos va a ser:</p> <ul> <li>Crear una nueva tabla en la base de datos de Drupal.</li> <li>Listar todos los registros de la tabla</li> <li>Crear nuevos registros mediante un formulario</li> <li>Modificar los datos de un registro</li> <li>Eliminar registros de la tabla</li> </ul> <p>Voy a partir el ejercicio en <a href="/blog/acceso-base-datos-en-drupal-7-parte-2">dos artículos</a> para que no me quede un ladrillo infumable, a ver si lo consigo.</p> <p>Antes de empezar un par de cosas:</p> <ul> <li>Estoy usando un Drupal 7 corriente y moliente. Para este ejemplo no necesitamos instalar ninguna dependencia (de módulos).</li> <li>El tema de presentación que uso es <a href="https://www.drupal.org/project/bootstrap">Bootstrap</a>, así que el HTML que vamos a ver usará algunas clases de Bootstrap que nos ayudará un poco con la presentación. No es realmente necesario y el HTML tendréis que adaptarlo según vuestras necesidades.</li> <li>Todos los nombres de campos, tablas, etc. van a ir <em>en inglés</em>. Los textos de la interfaz (botones, etiquetas, etc.) también, ya que vamos a usar siempre la función <em>t() </em>de Drupal para traducir.</li> </ul> <p>Comencemos.</p> <p> </p> <h2>Creando nuestro módulo</h2> <p>Para el ejemplo vamos a crear un nuevo módulo para el control de nuestra tabla de incidencias, que llamaremos <em>issue_control</em>. Lo creamos en el directorio estándar:</p> <pre> sites/all/modules/custom/issue_control</pre> <p>Tan sólo vamos a necesitar tres archivos:</p> <ul> <li><em>issue_control.info</em>: Archivo de control del módulo.</li> <li><em>issue_control.module</em>: Aquí tendremos nuestro código PHP.</li> <li><em>issue_control.install</em>: Lo usaremos para definir la tabla en la base de datos.</li> </ul> <p>Para empezar, será suficiente con completar el archivo <em>info</em>, con algo parecido a esto:</p> <pre> name = Issues control project = "issue_control" description = "Store information into a database table" package = Custom core = 7.x version = "7.x-0.0-rc1"</pre> <p>De momento, con esto nos vale para comenzar. Ojo, es mejor <strong>no instalar o activar el módulo todavía</strong>, hasta que tengamos la definición del esquema de base de datos. Vamos a ello, a ver como creamos nuestra tabla.<br />  </p> <h2>Hook Schema: Creando nuestra tabla</h2> <p>Perfecto, ya tenemos nuestro módulo. Ahora la idea es que al instalar el módulo, este nos cree automáticamente la tabla que necesitamos en la base de datos. Para ello vamos a usar el <a href="https://api.drupal.org/api/drupal/modules%21system%21system.api.php/function/hook_schema/7.x">hook_schema()</a>, que vamos a implementar en el archivo <em>issue_control.install</em> que acabamos de crear.</p> <p>Pero primero vamos a ver que campos necesitamos en nuestra tabla. Hagamos algo sencillo:</p> <table class="table table-striped"> <thead> <tr> <th scope="col">Campo</th> <th scope="col">Tipo</th> <th scope="col">Nulo</th> <th scope="col">Descripción</th> </tr> </thead> <tbody> <tr> <td>issue_id</td> <td>Int</td> <td>NO</td> <td>El identificador de la incidencia. Debería ser una secuencia o un <em>autoincrement</em>. No puede ser nulo, ya que lo vamos a usar como identificador y <em>primary key</em>. En Drupal usaremos el tipo <em>serial</em>, que nos permite hacer justamente eso y no tener que preocuparnos de rellenarlo nosotros.</td> </tr> <tr> <td>title</td> <td>String (255)</td> <td>NO</td> <td>Título de la incidencia. Una cadena de caracteres no muy larga. No queremos que se quede vacío. En Drupal usaremos el tipo <em>varchar</em>.</td> </tr> <tr> <td>description</td> <td>String (Largo)</td> <td> </td> <td>Este será el campo para guardar el contenido de la incidencia. Queremos que sea una <em>text area</em> donde el usuario pueda meter mucho texto. Para esto en Drupal tenemos el tipo <em>text</em>.</td> </tr> <tr> <td>uid</td> <td>Int</td> <td>NO</td> <td>Vamos a incluir el id del usuario (<em>uid</em>) que crea o modifica la incidencia, para que nos dé un poco de juego el ejercicio. Este sera un campo numérico sencillo de tipo <em>int </em>en nuestro Drupal.</td> </tr> </tbody> </table> <p>Bien, ahora que tenemos mas o menos claro los campos que queremos, vamos a crear nuestro <em>hook_schema</em>. Este hook debe ser implementado en el archivo <em>.install</em> del módulo, y nos va a permitir declarar una o varias tablas que se crearán cuando se instale el módulo (ojo: instalación, no activación) y se eliminarán automáticamente cuando se desinstale (ojo otra vez: desinstalar, no deshabilitar).</p> <p>Vamos a ver el código:</p> <pre> &lt;?php /** * Implements hook_schema(). */ function issue_control_schema() { $schema['issues'] = array( 'description' =&gt; 'Table for storing issues', 'fields' =&gt; array( 'issue_id' =&gt; array( 'type' =&gt; 'serial', 'unsigned' =&gt; TRUE, 'not null' =&gt; TRUE, 'description' =&gt; 'Issue ID', ), 'title' =&gt; array( 'type' =&gt; 'varchar', 'length' =&gt; 255, 'not null' =&gt; TRUE, 'default' =&gt; '', 'description' =&gt; 'Issue title', ), 'description' =&gt; array( 'type' =&gt; 'text', 'size' =&gt; 'normal', 'description' =&gt; 'Issue description', ), 'uid' =&gt; array( 'type' =&gt; 'int', 'unsigned' =&gt; TRUE, 'not null' =&gt; TRUE, 'default' =&gt; 0, 'description' =&gt; 'User uid for the issue', ), ), 'primary key' =&gt; array('issue_id'), 'indexes' =&gt; array( 'issueid' =&gt; array('issue_id'), ), ); return $schema; } </pre> <p>La referencia del <a href="https://api.drupal.org/api/drupal/includes%21database%21schema.inc/group/schemaapi/7.x">Schema API</a> de Drupal tiene bastante miga, y daría para varios artículos, pero no es el objetivo de este post. Recomiendo darle un buen repaso a la documentación, aunque no he podido encontrarla en castellano.</p> <p>Es importante tener en cuenta que si tenéis errores en este fichero o en este hook, lo más probable es que pete al instalar el módulo y no cree nada. Para que Drupal vuelva a ejecutar el <em>hook_schema</em> después de una prueba fallida, teneis que <strong>desinstalar el módulo por completo y volver a instalar</strong>. Podeis usar <em>drush pmu issue_control</em>, o bien utilizar el módulo <a href="https://www.drupal.org/project/devel">Devel</a>, que tiene una opción para reinstalar módulos muy útil para esto, hasta que consigáis que funcione correctamente la creación de la tabla.</p> <p> </p> <h2>Listado de registros de la tabla</h2> <p>Lo primero que vamos a hacer es preparar una página para mostrar el listado de las incidencias guardadas en la tabla. Logicamente, al principio estará vacía si acabamos de instalar el módulo. Podemos crear algunos registros a mano, o bien utilizando una herramienta como <a href="https://www.phpmyadmin.net/">phpMyAdmin</a>. Una ejemplo sencillo para tener algunos datos de prueba podría ser usando <em>Drush</em>:</p> <pre> drush sql-cli insert into issues (title, description, uid) values ('Un titulo', 'Description de pruebas', 1); </pre> <p>Para hacer nuestro listado vamos a utilizar dos funciones en nuestro archivo <em>.module</em>:</p> <ul> <li>Un <em>hook_menu</em> para definir la URL que usaremos para la lista, y la función a llamar para pintarla.</li> <li>Una función nuestra que devolverá el listado de incidencias en formato HTML.</li> </ul> <p>Primero, vamos a crear un <em>hook_menu</em>, y definiremos la URL <strong><em>/issues/list</em></strong> para nuestro listado de incidencias. De esta forma, cuando carguemos la URL <em><a href="http://misitio.drupal/issues/list">http://misitio.drupal/issues/list</a></em>, obtendremos la lista de incidencias.</p> <p>En nuestro archivo <em>.module</em>, declaramos el <em>hook_menu</em>:</p> <pre> /** * Implements hook_menu(). * */ function issue_control_menu() { $items = array(); $items['issues/list'] = array( 'title' =&gt; 'Issues List', 'description' =&gt; 'Issues list from the database.', 'page callback' =&gt; 'issue_control_list', 'page arguments' =&gt; array(), 'access callback' =&gt; TRUE, ); return $items; }</pre> <p>Con este <em>hook</em> definimos la URL y especificamos que al cargar dicha URL, se ejecutará la función <em>issue_control_list()</em> para pintar los resultados.</p> <p>Después implementamos esa función para listar:</p> <pre> /** * Shows a list with all issues in the database table. */ function issue_control_list() { $content = '&lt;table class="table table-hover table-striped"&gt;'; $content .= '&lt;tr&gt;'; $content .= '&lt;th&gt;' . t('Issue Id') . '&lt;/th&gt;'; $content .= '&lt;th&gt;' . t('Title') . '&lt;/th&gt;'; $content .= '&lt;th&gt;' . t('Description') . '&lt;/th&gt;'; $content .= '&lt;th&gt;' . t('Username') . '&lt;/th&gt;'; $content .= '&lt;th&gt;' . t('Actions') . '&lt;/th&gt;'; $content .= '&lt;/tr&gt;'; $results = db_query('SELECT * FROM {issues}'); foreach ($results as $row) { $user = user_load($row-&gt;uid); $content .= '&lt;tr&gt;'; $content .= '&lt;td&gt;' . $row-&gt;issue_id . '&lt;/td&gt;'; $content .= '&lt;td&gt;' . $row-&gt;title . '&lt;/td&gt;'; $content .= '&lt;td&gt;' . $row-&gt;description . '&lt;/td&gt;'; $content .= '&lt;td&gt;' . (!empty($user-&gt;name) ? $user-&gt;name : t('Anonymous')) . '&lt;/td&gt;'; $content .= '&lt;td class="btn-toolbar"&gt;'; $content .= '&lt;a class="btn btn-xs btn-primary" href="' . url('/issues/edit/' . $row-&gt;issue_id) . '"&gt;' . t('Edit') . '&lt;/a&gt;'; $content .= '&lt;a class="btn btn-xs btn-danger" href="' . url('/issues/delete/' . $row-&gt;issue_id) . '"&gt;' . t('Delete') . '&lt;/a&gt;'; $content .= '&lt;/td&gt;'; $content .= '&lt;/tr&gt;'; } $content .= '&lt;/table&gt;'; $content .= '&lt;a class="btn btn-success" href="' . url('/issues/create') . '"&gt;' . t('Add an issue') . '&lt;/a&gt;'; return $content; }</pre> <p>Con este código estamos obteniendo todos los registros de la tabla issues y pintando una tabla HTML para mostrar los resultados. Esta función tiene varias partes interesantes.</p> <ol> <li>Se utiliza una variable <em>$content</em> para ir guardando el HTML que vamos construyendo.</li> <li>Primero creamos las cabeceras de la tabla con los nombres de los campos.</li> <li>Después, se lanza la consulta a la base de datos para pedir todos los registros de la tabla <em>issues.</em></li> <li>A continuación se utiliza un bucle para pintar cada uno de los registros. Para cada registro cargaremos el usuario de Drupal correspondiente al <em>uid</em> que venga en la issue, y pintamos su nombre.</li> <li>Las clases CSS que estoy utilizando son para <em>Bootstrap</em>, por ejemplo, los <em>table</em> y <em>table-striped</em> nos proporcionarán una tabla más pintona, o los <em>btn</em> y <em>btn-primary</em> para los botones de acción.</li> </ol> <p> </p> <h2>Obteniendo los datos</h2> <p>Vamos a ver un poco mas en detalle cómo estamos obteniendo los registros de la base de datos. En este caso como es una <em>query</em> muy sencilla, usaremos la función <a href="https://api.drupal.org/api/drupal/includes%21database%21database.inc/function/db_query/7.x">db_query</a> de Drupal:</p> <pre> $results = db_query('SELECT * FROM {issues}');</pre> <p>Esta llamada nos devuelve el resultado de la <em>query</em> como un array en la variable <em>$results</em>. Un detalle importante es utilizar el nombre de la tabla entre llaves, ya que Drupal lo reemplazará por la tabla correcta si nuestro sitio usa un prefijo (<em>prefix</em>) para la base de datos.</p> <p>Este array tendrá un objeto de tipo <em>stdClass</em> por cada registro de la base de datos que devuelva la consulta, y en cada uno de estos objetos podremos acceder a los campos de la tabla como si fuesen propiedades, por ejemplo <em>$row-&gt;title</em>, nos devolverá el título. </p> <p>De esta forma, para leer y pintar los registros usaremos un bucle, e iremos recogiendo los valores de cada campo que queremos mostrar:</p> <pre> foreach ($results as <strong>$row</strong>) { $user = user_load(<strong>$row-&gt;uid</strong>); // Aquí cargamos el usuario correspondiente por su uid.   $content .= '&lt;tr&gt;'; $content .= '&lt;td&gt;' . <strong>$row-&gt;issue_id</strong> . '&lt;/td&gt;'; // Pintamos el campo issue_id. ... } </pre> <p>No debemos usar <em>db_query</em> para realizar operaciones de <em>insert</em>, <em>update</em> o <em>delete</em>, y si necesitamos realizar operaciones con la query antes de ejecutarla, es mejor usar la función <em>db_select</em> que veremos más adelante.</p> <p> </p> <h2>Resultados</h2> <p>Si todo ha ido bien tendremos algo parecido a este ejemplo que se ve bastante resultón gracias a <em>Bootstrap</em>:</p> <div alt="Listado de incidencias" data-embed-button="file_browser" data-entity-embed-display="image:image" data-entity-embed-display-settings="full_width_shadow" data-entity-type="file" data-entity-uuid="12b43b0e-79fd-423c-9406-77c3bc0a05a8" title="Listado de incidencias" data-langcode="es" class="embedded-entity"> <img src="/sites/default/files/styles/full_width_shadow/public/listado_de_incidencias_com_tema_bootstrap.png?itok=KcEHMVOo" alt="Listado de incidencias" title="Listado de incidencias" typeof="foaf:Image" class="img-responsive" /> </div> <p>Como podéis ver, estamos ya pintando los botones de acción para cada registro: <em>Edit</em> y <em>Delete</em>, que son enlaces a otras páginas que aún no existen, y después de la tabla tenemos otro botón para crear una incidencia nueva. Estos enlaces serán:</p> <ul> <li>Un enlace <em>/issues/edit/[issue_id]</em> para modificar los datos de una incidencia.</li> <li>Otro a <em>/issues/delete/[issue_id]</em> para eliminar incidencias de la tabla.</li> <li>Por último, <em>/issues/create</em> para dar de alta incidencias desde un formulario de Drupal.</li> </ul> <p>Estas tres acciones las veremos en el próximo artículo, en el que os dejaré un enlace para descargar todo el código del ejemplo completo.</p> <p>Como pequeña puntualización, esta función para pintar la lista tiene demasiado código HTML. Lo correcto sería <em>utilizar una plantilla o template</em> para pintar la lista, de forma que todo el HTML estaría dentro del <em>template</em>. Hay mil ejemplos en internet para utilizar <em>templates</em> en nuestros módulos con Drupal 7, pero aún así pienso escribir otro artículo después de estos para mostrar cómo hacerlo en condiciones con este ejemplo.</p> <p>Muchas gracias y hasta pronto.</p> <p> </p> <p><a href="/blog/acceso-base-datos-en-drupal-7-parte-2">La segunda parte de este artículo esta aquí.</a></p> </div> </div> <div class="group-footer"> <section> <h2>Añadir nuevo comentario</h2> <drupal-render-placeholder callback="comment.lazy_builders:renderForm" arguments="0=node&amp;1=36&amp;2=comment&amp;3=comment" token="5eDklJfT4NxvvLxFLpKSLCtmXzwwAdpl5L5jpEDPysM"></drupal-render-placeholder> </section> </div> </article> Sat, 11 Nov 2017 13:50:48 +0000 root 36 at https://www.carloscarrascal.com