Primeros pasos con MongoDB en CakePHP

En el anterior artículo, dejábamos configurada la conexión al motor de base de datos MongoDB en CakePHP. En este artículo, haremos los primeros trabajos con documentos y colecciones para ver cómo reacciona Cake ante este sistema de base de datos NoSQL.

Vamos a empezar desde la consola de MongoDB para empezar a construir nuestra estructura de datos. Para ello abrimos una ventana de la consola del sistema y ejecutaremos el cliente mongo.exe (si no está la ruta de la instalación de mongo en la variable de sistema “Path”, habrá que acceder a su directorio para ejecutar el cliente). Una vez en la consola de MongoDB, realizaremos las siguientes operaciones:

> use pruebamongo [Intro] > db.users.insert({ "name" : "John Doe", "email" : "jdoe@email.com"}) [Intro] 

Con la primera función, indicamos la base de datos que vamos a utilizar. Con la segunda insertamos un documento en la colección users. Al realizar estas dos operaciones, MongoDB ha creado la base de datos “pruebamongo” y la colección “users” de forma automática.

Ahora llega el primer cambio que debemos asumir al utilizar MongoDB en CakePHP, y es dejar de utilizar el comando bake para “cocinar” nuestros modelos, controladores y vistas. Si ejecutamos el comando bake, se nos presentará una información errónea, ya que no habrá tablas que seleccionar de la base de datos.

Seisunos - blog cake bake

Creamos pues el modelo, el controlador y la vista “index” para gestionar la colección “users”.

\app\Model\User.php

<?php
class User extends AppModel {
public $primaryKey = '_id';
}

\app\Controllers\UsersControllers.php

App::uses('AppController', 'Controller');
class UsersController extends AppController {
    public $components = array('Paginator');
    public function index() {
        
        $this->User->recursive = 0;
        
        $this->Paginator->settings = array(
                'fields' => array('name', 'email', 'created', 'modified'),
                'order' => array('User.name' => 'ASC')
        );
        
        $this->set('users', $this->Paginator->paginate());
    }
}

\app\Views\Users\index.ctp

<div>
    <?php echo $this->Html->link(__('New'), array('action' => 'add'), array('class' => 'small button radius secondary')); ?>
</div>
    
<div>
    <table>
    <tr>
        <th><?php echo $this->Paginator->sort('_id', 'id'); ?></th>
        <th><?php echo $this->Paginator->sort('name'); ?></th>
        <th><?php echo $this->Paginator->sort('email'); ?></th>
        <th><?php echo $this->Paginator->sort('created'); ?></th>
        <th><?php echo $this->Paginator->sort('updated'); ?></th>
    </tr>
    <?php foreach ($users as $user): ?>
    <tr>
        <td><?php echo $user['User']['_id']; ?>&nbsp;</td>
        <td><?php echo $this->Html->link($user['User']['name'], array('action' => 'view', $user['User']['_id']), array('title'=>__('User details'), 'escape'=>false)); ?></td>
        <td><?php echo $user['User']['email']; ?></td>
        <td><?php echo $user['User']['created']; ?></td>
        <td><?php echo $user['User']['updated']; ?></td>
    </tr>
    <?php endforeach; ?>
    </table>            
</div>
    
    
<p>
    <?php 
    echo $this->Paginator->counter(array(
        'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}')
    ));
    ?>
</p>
<div class="paging">
    <?php
        echo $this->Paginator->prev('< ' . __('previous'), array(), null, array('class' => 'prev disabled'));
        echo $this->Paginator->numbers(array('separator' => ''));
        echo $this->Paginator->next(__('next') . ' >', array(), null, array('class' => 'next disabled'));
    ?>
</div>

Ahora vemos el resultado en nuestra aplicación, en la dirección localhost/pruebamongo/users. Éste es el resultado:

Seisunos - blog cakephp

En esta página resaltan tres aspectos. Por un lado, Mongo ha creado un identificador para el usuario que añadimos desde su cliente. Los documentos en MongoDB deben contener un identificador (_id) que, como el resto de datos, puede ser del tipo que queramos (excepto un array). No obstante, si no especificamos ningun id, MongoDB propone uno como ObjectId, al que es interesante dedicar una breve revisión.

El ObjectId es de tipo BSON de 12 bytes, dividido en cuatro bloques:

4 bytes referentes al tiempo Unix
3 bytes referentes al identificador de la máquina
2 bytes referentes al ID del proceso
3 bytes que recogen un contador aleatorio

De esta forma, la mayor relevancia la tendrán los 4 bytes del tiempo, lo que asegura que una ordenación por _id estará ordenada también temporalmente. Si se hacen dos inserciones en el mismo segundo desde distintas máquinas, el segunda bloque será el encargado de identificar unívocamente el documento. Si, por el contrario, se trata de dos inserciones desde el mismo equipo, será el tercer bloque el que los identifique. Y, si todo coincide, será mediante el cuarto bloque como quedarán identificados.

Esta fórmula hace extremadamente difícil que dos documentos lleguen a tener un mismo _id.

CakePHP trabajará correctamente con estos identificadores en el momento en que hacemos la declaración en el modelo:

public $primaryKey = '_id';

En segundo lugar, otro aspecto a destacar en nuestra aplicación, es la presencia de dos cuadros de error, debido a que, cuando hemos añadido el documento desde la consola de mongo, no hemos especificado los campos “created” ni “modified”. Impelementemos ahora la funcionalidad para añadir un nuevo “user” desde nuestra aplicación.

Añadimos en \app\Controllers\UsersControllers.php

    public function add() {
        if ($this->request->is('post')) {
            $this->User->create();
            if ($this->User->save($this->request->data)) {
                $this->Session->setFlash(__('The user has been saved.'));
                return $this->redirect(array('action' => 'index'));
            } else {
                $this->Session->setFlash(__('The user could not be saved. Please, try again.'));
            }
        }
    }
    

Creamos el fichero \app\Views\add.php

<div>
    <?php echo $this->Html->link(__('List'), array('action' => 'index'), array('class' => 'small button radius secondary')); ?> 
</div>
<div>
    <?php 
    echo $this->Form->create('User');
    echo $this->Form->input('name');
    echo $this->Form->input('email');
    echo $this->Form->end(array('label' => __('Save')));
    ?>
</div>

Y añadimos un usuario nuevo:

Seisunos - blog cakephp

seisunos - blog cakephp

Como se ve en la última imagen, hemos añadido el nuevo usuario y Cake ha insertad las fechas “created” y “modified”. Si vemos ahora el contenido de la colección en la consola de mongo:

Seisunos - blog mongodb

Vemos lo que mencionábamos en un post anterior respecto al polimorfismo que soportan los documentos en MongoDB. Como se puede ver en la imagen, el primer registro sólo contiene los datos “_id”, “name” y “email”, pero el segundo tiene además los datos “created” y “modified”. Si hacemos uso de este polimorfismo en nuestra aplicación, deberemos llevar controles sobre los datos a los que accedemos, ya que podemos tratar con documentos de estructura diferente, algo que no sucedía con las bases de datos SQL.

Las fechas “created” y “modified” son de tipo ISODate, otra variación de usar MongoDB en CakePHP, y para presentarla basta con acceder a sus segundos:

echo date('Y-m-d H:i:s', $user['User']['created']->sec);

Y el último aspecto a destacar del uso del plugin MongoDB, es la parte del dump de consultas. Como podemos ver, ya no se trata de las consultas habituales de bases de datos SQL, sino que se trata de llamadas a las funciones de MongoDB:

Seisunos - blog cakephp

Si bien hay cambios que debemos asumir en el momento de utilizar bases de datos NoSQL, utilizar MongoDB en Cakephp no tiene por qué ser dramático y vamos a seguir contando con la ventaja de la “magia” que aporta CakePHP. En próximos artículos ahondaremos en el uso de MongoDB en CakePHP.

Referencias:
http://www.tutorialspoint.com/mongodb/mongodb_objectid.htm

Related posts

Leave a Reply