Ya hace unos años que hago esto y la verdad es que es muy práctico. Consiste básicamente en subir automáticamente las partidas guardadas a un servicio en la nube como Dropbox, Mega u ownCloud.

Me voy a centrar en hablar de partidas guardadas, pero el caso es que lo que aprenderéis es a automatizar el proceso de copia en la nube, de modo que lo podéis aprovechar para hacer copia de más cosas (como de vuestros documentos más importantes).

Antes de empezar, aclarar que voy a hacer el tutorial para Mac, Linux y Win, pero en el caso de Windows todos los ejemplos serán con versiones posteriores a Windows Vista. Si estás utilizando Windows XP (seriously?) deberás buscar las ubicaciones equivalentes.

Básicamente necesitaréis una cuenta en cualquier servicio en la nube que tenga cliente de sincronización. ¿Que qué coño es eso de “cliente de sincronización”? Pues una aplicación que se encarga de tener sincronizados tus ficheros entre tu ordenador y el servicio de almacenaje en cuestión, a.k.a. “la nube”.

Así que lo primero es crearse la cuenta si no lo habéis hecho ya.

A continuación dejo enlazados los clientes de los tres servicios que he mencionado:

  • Dropbox: https://www.dropbox.com/install
  • Mega: https://mega.nz/#sync
  • ownCloud: https://owncloud.com/products/desktop-clients/

No es intención de esta guía explicar las diferencias entre ellos ni tampoco comparar sus características. Dejo a vuestra elección el servicio a elegir —en caso de no estar utilizando uno ya.

Ahora que ya tenéis cuenta en la nube y tenéis el cliente instalado, procedamos a hacer la copia en la nube.

Antes de proceder, necesito que entendáis mínimamente qué es lo que vamos a hacer, aunque si os la suda y queréis hacer el mono copiando-pegando podéis pasar a la parte final.

Lo que vamos a hacer es crear un “enlace simbólico” (symlink en inglés) en la carpeta de Dropbox que enlace hacia la carpeta donde estén las partidas guardadas en cuestión.

En sistemas Unix (como Mac o Linux) un enlace simbólico suele ser lo mismo que un acceso directo (shortcut en inglés), pero en Windows no tienen nada que ver:

  • Un acceso directo es un fichero con extensión .lnk. Si creamos un acceso directo a una partida guardada en nuestro servicio en la nube lo que estaremos haciendo es subir un fichero con estensión .lnk a dropbox. Vamos, algo inútil.
  • Un enlace simbólico no tiene extensión específica, pero siempre comparte las características de aquello que hemos enlazado. Esto quiere decir que si enlazamos simbólicamente una carpeta nuestro sistema se creerá que esa carpeta está ahí. O dicho de otro modo, si hacemos un enlace simbólico hacia una carpeta en nuestro dropbox, dropbox creerá que es una carpeta, subiendo los contenidos de ésta (pero en nuestro sistema sólo nos ocupará espacio la carpeta real).

Dicho esto, da igual el orden en que hagáis el enlace simbólico; podéis tener las carpetas originales en el servicio de la nube y enlazar hacia la carpeta de la partida guardada (es lo que hago yo cuando instalo un juego del que ya tengo partida guardada) o bien puede ser al revés: que tengáis la partida guardada en su directorio original y creéis un enlace hacia ella en el servicio de la nube (es lo que hago la primera vez que instalo un juego).

Vamos, que podéis utilizar uno u otro indistintamente, pero en mi caso utilizo según me conviene, por comodidad. Así no tengo que ir moviendo ficheros de un sitio a otro.

Al grano

Bueno, ahora sólo nos queda una cosa: crear el enlace simbólico.

Para ello necesitas saber la ubicación exacta de:

  • La partida guardada a salvar en la nube (cada juego guarda donde le da la puta gana).
  • La ubicación en tu servicio de la nube donde lo quieras almacenar.

Os recomiendo que creéis una carpeta en vuestro servicio en la nube para tenerlo todo bien agrupado. En los ejemplos utilizaré la ubicación C:\Users\USUARIO\ownCloud\Saved Games

El primer ejemplo lo haré con The Witcher 3. Las partidas guardadas están en Mis Documentos/The Witcher 3 que, si no habéis cambiado la ubicación de la carpeta Mis Documentos debería ser:

C:\Users\USUARIO\Documents\The Witcher 3

Vale, ahora viene la parte que no he avisado y a la que muchos tendréis miedo: la consola.

Abrid una consola con permisos de administración (tecla de Windows, escribes cmd y le das a Shift + Enter) y teclead lo siguiente:

1
mklink /?

El comando en cuestión es mklink y al pasarle /? como parámetro, os devolverá la ayuda, que debería ser algo parecido a esto:

1
2
3
4
5
6
7
8
9
10
11
Crea un vínculo simbólico.

MKLINK [[/D] | [/H] | [/J]] Vínculo Destino

        /D      Crea un vínculo simbólico a un directorio. El valor
                predeterminado es un vínculo simbólico a un archivo.
        /H      Crea un vínculo físico en lugar de un vínculo simbólico.
        /J      Crea una unión de directorios.
        Vínculo Especifica el nombre del nuevo vínculo simbólico.
        Destino Especifica la ruta (relativa o absoluta) a la que hace
                referencia el nuevo vínculo.

Tal como pone en la ayuda, tendremos que indicar tres parámetros:

  • /D para indicar que estamos haciendo un enlace simbólico de un directorio.
  • Vínculo la ubicación del vínculo simbólico.
  • Destino a qué enlaza ese vínculo simbólico.

Así pues, siguiendo el ejemplo de The Witcher 3, en mi caso debería hacer:

1
mklink /D "C:\Users\USUARIO\ownCloud\Saved Games\The Witcher 3" "C:\Users\USUARIO\Documents\The Witcher 3"

No hace falta entrecomillar los parámetros si la ubicación no tiene espacios.

Y listos. Como resultado la consola os indicará que el enlace simbólico ha sido creado.

Sistemas UNIX (Linux / Mac)

En el caso de los sistemas UNIX el comando a utilizar es ln. Utilizad ln --help para ver todas las opciones posibles (es bastante más extenso que el comando MKLINK de Windows).

Siguiendo el ejemplo de Windows, aquí tenéis que tener en cuenta que los argumentos Vículo y Destino están intercambiados y que habrá que utilizar la opción -s para indicarle que es un enlace simbólico.

Así pues, siguiendo el ejemplo de Windows de The Witcher 3 (que por desgracia no está para sistemas Unix), sería:

1
ln -s  /home/USUARIO/Documents/The\ Witcher\ 3 /home/USUARIO/ownCloud/Saved\ Games/The\ Witcher\ 3

Más ejemplos

Para no liar seguiré utilizando la misma ubicación para mis partidas guardadas, C:\Users\USUARIO\ownCloud\Saved Games.

Rockstar Games / GTA

En este caso copio la carpeta Rockstar Games entera:

1
mklink /D "C:\Users\USUARIO\ownCloud\Saved Games\Rockstar Games" "C:\Users\USUARIO\Documents\Rockstar Games"

Unix:

1
ln -s /home/USUARIO/Documents/Rockstar\ Games /home/USUARIO/ownCloud/Saved\ Games

Tropico 4 / 5

1
2
mklink /D "C:\Users\USUARIO\ownCloud\Saved Games\Tropico 4" "C:\Users\USUARIO\AppData\Roaming\tropico 4"
mklink /D "C:\Users\USUARIO\ownCloud\Saved Games\Tropico 5" "C:\Users\USUARIO\AppData\Roaming\tropico 5"

Unix:

1
2
ln -s /home/USUARIO/Documents/Tropico\ 4 /home/USUARIO/ownCloud/Saved\ Games
ln -s /home/USUARIO/Documents/Tropico\ 5 /home/USUARIO/ownCloud/Saved\ Games

Más usos

  • Copias de seguridad de todo tipo.
  • Mover una aplicación/juego entre discos duros y crear un enlace simbólico evitará que tengáis que reinstalarlo o reubicar sus accesos directos.
  • Organizar ficheros o directorios.
  • Compartir ficheros en múltiples directorios sin ocupar el espacio que todos ellos requieren y teniendo todos los ficheros en cuestión actualizados.

En esta guía os explicaré cómo realizar la instalación de un servidor Windows Apache + MySQL + PHP (WAMP) con múltiples versiones de PHP para distintos servidores virtuales.

En realidad me centraré en la configuración tanto de Apache como de PHP para que tengáis todo funcionando como es debido, de aquí que diga lo de “a pelo”.

Instalaréis todo a partir de las páginas oficiales, tal como, creo, debería de ser siempre (y dejarse estar de mierdas prefabricadas).

Antes de empezar quiero dejar claro que ni soy de Microsoft, ni de Apache, ni de MySQL, ni de PHP; así que si tenéis algún problema no soy la persona a quién deberéis preguntar.

La metodología que explicaré a continuación la he utilizado anteriormente con éxito en múltiples máquinas y en varios sistemas operativos además de la instalación que realizo en una máquina virtual mientras os explico este tutorial paso por paso.

El proceso lo voy a realizar desde Windows 7 x64, pero como he dicho lo he tenido funcionando en varios sistemas operativos sin problemas:

  • Windows XP x86
  • Windows 7 x64 y x86
  • Windows 8 x64
  • Windows 8.1 x64
  • Windows 10 x64

Como podéis ver, en la versión 32 bits de windows 8 no lo he probado pero, como digo, debería funcionar (me pregunto quién corre un Win8 a 32 bits).

En cuanto a las versiones de software, yo personalmente recuerdo haber probado, al menos:

  • Apache 2.0, 2.2 y 2.4
  • MySQL 5.5
  • PHP 5.3, 5.4, 5.5, 5.6 y 7

De hecho yo en mi ordenador tengo todas esas versiones de PHP instaladas pero, por motivos obvios, en este tutorial voy a utilizar la última versión de apache y las dos últimas versiones de PHP (el mínimo necesario para poder enseñaros cómo configurar múltiples versiones por virtualhost).

Todo ello utilizando las versiones de 64 bits, pero procuraré poneros los enlaces actuales para las versiones de 32 bits.

Antes de empezar es importantísimo que comprobéis que tenéis todas las actualizaciones del sistema al día. Especialmente interesa tener .NET Framework instalado y actualizado para poder correr cualquiera de las aplicaciones que vamos a instalar. En mi caso la última actualización fue .NET Framework 4.5.2.

Estructura de la instalación

Podéis escoger instalar cada cosa donde os dé la gana, pero procurad tenerlo en cuenta a la hora de configurar cada cosa.

Para hacer las cosas sencillas y por tenerlo todo ordenado yo siempre he seguido esta estructura de directorios:

1
2
3
C:\httpd\apache -> Apache 2.4
C:\httpd\phpXX  -> Cada una de las versiones de PHP
C:\httpd\www    -> Directorio raíz de todos mis proyectos en desarrollo

MySQL dejo que se instale donde quiera ya que no me preocupa tener copia de sus ficheros. Evidentemente hago copia de seguridad de las bases de datos pero no es el tema que nos concierne ahora.

Instalando Apache

Desde hace relativamente poco la descarga de binarios de Apache ya no se realiza desde el sitio oficial. En su lugar ellos mismos te recomiendan varios sites alternativos desde los que efectuar la descarga.

Podéis ver esto desde cualquiera de los mirrors de descarga de apache, bajo binaries/win32.

Yo personalmente recomiendo ApacheLounge y os guiaré para descargar desde ahí los ficheros.

Os guiaré, pero de todos modos pondré los enlaces directos a los ficheros que yo he utilizado (no os lo recomiendo ya que sacan versiones nuevas constantemente).

Entrad en ApacheLounge y seguid estos pasos para descargar todo lo necesario:

  • Id a descargas. En la barra lateral veréis que pone "VC11" y "VC10", eso es la versión necesaria del compilador de Visual C++ proporcionado por Microsoft.
  • Descargad la versión que os interese de Apache, en mi caso descargo la versión 2.4.12 Win64 desde VC11.
  • Dependiendo de la versión que hayáis descargado tenéis que descargar el compilador de C++. Veréis que está enlazado cerca de la misma descarga de Apache. En mi caso descargo VC11 vcredist_x64/86.exe asegurándome de marcar la casilla `VSU_4\vcredist_x64.exe` ya que estoy descargando para 64 bits.
  • Descargad también el módulo de apache `mod_fcgid`. En el caso de Apache 2.4 los módulos vienen todos en un mismo zip así que en mi caso descargo modules-2.4-win64-VC11.zip.

Ahora que ya tenemos todo lo necesario en cuanto a Apache se refiere, procederemos a instalarlo.

Instalad primero VC10 (u 11) ya que es requisito indispensable para que Apache funcione.

Una vez instalado, descomprimid Apache y movedlo donde lo queráis instalar. En mi caso descomprimo apache en C:\httpd\apache de modo que accediendo a dicho directorio me queda el contenido del .zip (múltiples directorios y ficheros, entre ellos INSTALL.txt con detalles sobre cómo instalar Apache).

Como en mi caso no quiero utilizar el directorio htdocs que viene dentro del directorio de apache creo ahora el directorio C:\httpd\www.

Descomprimid también el módulo mod_fcgid. Os interesa únicamente el fichero mod_fcgid.so que desplazaréis dentro del directorio modules del directorio raíz de apache, en mi caso C:\httpd\apache\modules quedando C:\httpd\apache\modules\mod_fcgid.so listo para ser cargado por apache.

Hecho esto necesitáis configurar Apache para indicarle el directorio donde lo habéis ubicado. Para ello abrid el fichero C:\httpd\apache\conf\httpd.conf con el bloc de notas (o con vuestro editor de código favorito) —huelga decir que editar ese fichero con Word o WordPad sería una idiotez.

Cerca de la línea 37 encontraréis:

1
ServerRoot "C:/Apache24"

Debéis cambiarlo a vuestra ubicación que siguiendo esta guía sería:

1
ServerRoot "C:/httpd/apache"

Para ir más rápido, buscad todas las coincidencias en el documento de C:/Apache24 y reemplazadlas con vuestra ubicación de instalación. Esto debería de reemplazar varias cosas como el DocumentRoot o la ubicación de los cgi-bin.

Dado que, en mi caso, el DocumentRoot lo quiero en otra ubicación, lo cambio en los dos sitios donde requiere (líneas 242 y 243 aproximadamente) a C:\httpd\www.

En esa misma configuración de Directory (cerca de la línea 243) debéis aseguraros de que se pueden ejecutar scripts cgi, dado que utilizaréis fastcgi para ejecutar PHP. Para ello debéis añadir ExecCGI detrás de las opciones, quedando esa línea así:

1
Options Indexes FollowSymlinks ExecCGI

Ahora, cerca de la línea 176, cuando terminan de cargar todos los módulos debéis añadir el módulo mod_fcgid tal que así:

1
LoadModule fcgid_module modules/mod_fcgid.so

También sería un buen momento para que revisárais qué módulos de los que hay comentados os interesa tener habilitados para descomentarlos (como el típico mod_rewrite).

Para avanzar faena, cambiad DirectoryIndex para que coja ficheros php por defecto:

1
DirectoryIndex index.php index.html index.htm

Al final de todo del documento añadid las siguientes líneas:

1
2
3
4
5
6
7
8
9
10
11
12
# 200MB of file upload limit
FcgidMaxRequestLen 209715200
# Set minimum timeouts
FcgidConnectTimeout 240
FcgidIOTimeout 240
FcgidBusyScanInterval 240
FcgidBusyTimeout 240

# Set PHP 5.6 as default php-cgi executable
# AddHandler fcgid-script .php
# FcgidInitialEnv PHPRC "C:/httpd/php56"
# FcgidWrapper "C:/httpd/php56/php-cgi.exe" .php

El último trozo está comentado a propósito, ya que instalaremos y probaremos Apache antes de instalar PHP.

Ya lo tenéis todo listo, guardad el documento e id a ejecutar Apache. Para ello abrid un terminal (cmd o PowerShell, tanto da) y ejecutad lo siguiente:

1
cd C:\httpd\apache\bin

Ahora que estáis en el directorio de ejecutables de Apache ejecutadlo para ver si arranca:

1
.\httpd.exe

Ojo! Algunas aplicaciones como Skype utilizan el puerto 80 si ven que no está ocupado. Por ello es recomendable que cerréis cualquier aplicación que tengáis corriendo antes de ejecutar Apache. Si no lo hacéis simplemente os saltará un error indicando que dicho puerto está ocupado.

Si todo ha ido bien únicamente os aparecerá una alerta indicando que no se ha indicado un ServerName. Sino os mostrará claramente el error y deberéis de arreglarlo. En mi caso mientras hacía este tutorial olvidé crear el directorio www y me saltó este error:

1
2
Syntax error on line 243 of C:/httpd/apache/conf/httpd.conf:
DocumentRoot must be a directory

Utilizando vuestro navegador podéis acceder a http://localhost y comprobar que el servidor funciona correctamente.

Perfecto, pero no hemos instalado Apache. Sólo hemos comprobado que funcionaba correctamente antes de instalarlo.

Para instalarlo debéis ejecutar un terminal con derechos de administrador. Una vez abierta y habiendo accedido a C:\httpd\apache\bin ejecutad:

.\httpd.exe -k install

Para hacer que ApacheMonitor se ejecute cuando arranquéis el ordenador únicamente tenéis que crearle un acceso directo en la sección Inicio del menú Inicio (un poco redundante, sí).

En català no és pas tant redundant, oi?

Hecho. Si os da el gusanillo reiniciad el ordenador y acceded de nuevo a http://localhost para comprobar que todo sigue funcionando OK.

Instalando PHP

Id a la página de descargas de PHP para windows y descargad todas las versiones de PHP que queráis tener instaladas.

Al descargarlas únicamente debéis tener en cuenta la arquitectura que estéis instalando (en mi caso x64) y que Apache es Thread Safe.

Thread safe

En mi instalación descargo PHP 5.6 (5.6.6) VC11 x64 Thread Safe (2015-Feb-19 01:45:29) y PHP 5.5 (5.5.22) VC11 x64 Thread Safe (2015-Feb-19 01:38:01).

Una vez tenéis todo descargado, lo descomprimís y lo movéis hacia el directorio C:\httpd. Poned nombres cortos a los directorios:

1
2
C:\httpd\php56
C:\httpd\php55

Ya está PHP instalado, pero no configurado.

Si queréis poder ejecutar php.exe desde vuestro terminal deberéis añadir a la variable de entorno PATH el directorio de la versión que queráis utilizar. En caso de hacerlo también deberéis crear una nueva variable de entorno PHPRC con el mismo directorio que habéis añadido al PATH.

Ahora tenéis que entrar en cada instalación de php que habéis instalado y renombrar el fichero php.ini-development a php.ini y hacer los cambios que indicaré a continuación.

Buscad el siguiente trozo:

1
2
; On windows:
; extension_dir = "ext"

Y descomentad la línea de extension_dir, quedando así:

1
2
; On windows:
extension_dir = "ext"

Buscad el trozo donde estén todas las extensiones y descomentad las que os interesen. Las encontraréis en este formato:

1
2
3
4
;extension=php_bz2.dll
;extension=php_curl.dll
;extension=php_fileinfo.dll
;[...]

Estas tres primeras que he puesto yo seguramente os interesará activarlas, al igual que gd2, gettext, intl, mbstring, exif, mysql, mysqli, pdo_mysql y/u openssl.

Hecho esto en todos los ficheros php.ini de nuestras instalaciones, es momento de acabar de configurar apache para que ejecute ficheros php con nuestra versión por defecto de PHP.

Volved al fichero httpd.conf de apache y al final del documento, descomentad:

1
2
3
4
# Set PHP 5.6 as default php-cgi executable
AddHandler fcgid-script .php
FcgidInitialEnv PHPRC "C:/httpd/php56"
FcgidWrapper "C:/httpd/php56/php-cgi.exe" .php

Utilizando ApacheMonitor reiniciad Apache. Cread un fichero index.php en el directorio www con un phpinfo dentro:

1
2
<?php
phpinfo();

Y acceded a http://localhost para verificar que PHP funciona correctamente.

Utilizando distintas versiones de PHP en nuestros servidores virtuales

Ahora que ya tenéis Apache y la versión de PHP por defecto corriendo, llega el momento de explicaros cómo cambiar entre versiones de PHP.

Para utilizar esta metodología es necesario el uso de servidores virtuales, así que en el fichero httpd.conf descomentáis la línea donde carga el fichero de VirtualHosts:

1
2
# Virtual hosts
Include conf/extra/httpd-vhosts.conf

Descomentada esa línea abrís el fichero conf/extra/httpd-vhosts.conf en cuestión y creáis un par de servidores virtuales que irán con distintas versiones de PHP.

Para el ejemplo crearéis dos servidores virtuales php55.loc y php56.loc, así que lo primero que haréis es crear sus directorios:

1
2
C:\httpd\www\php55.loc
C:\httpd\www\php56.loc

Y en el fichero httpd-vhosts.conf previamente abierto creamos los servidores virtuales:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<VirtualHost *:80>
  # Indicamos que queremos utilizar PHP 5.5
  FcgidInitialEnv PHPRC "C:/httpd/php55"
  FcgidWrapper "C:/httpd/php55/php-cgi.exe" .php

  DocumentRoot "C:/httpd/www/php55.loc"
  ServerName php55.loc
  ServerAlias www.php55.loc
  ErrorLog "logs/php55.loc-error.log"
  CustomLog "logs/php55.loc-access.log" common
</VirtualHost>

<VirtualHost *:80>
  # Dado que utilizo PHP 5.6 por defecto no me es
  # necesario especificar nada a fastcgi

  DocumentRoot "C:/httpd/www/php56.loc"
  ServerName php56.loc
  ServerAlias www.php56.loc
  ErrorLog "logs/php56.loc-error.log"
  CustomLog "logs/php56.loc-access.log" common
</VirtualHost>

Para ir acabando añadid al fichero hosts de sistema (C:\Windows\System32\drivers\etc\hosts) los dominios de los servidores virtuales:

1
2
127.0.0.1    php55.loc www.php55.loc
127.0.0.1    php56.loc www.php56.loc

Copiad el fichero index.php que hemos creado antes en el directorio www a cada directorio de cada host virtual.

Finalmente reiniciad apache utilizando ApacheMonitor y acceded con vuestro navegador a http://php55.loc y http://php56.loc para verificar que todo ha funcionado correctamente.

Estoy seguro de que sí :)

php.loc

Instalar MySQL

Descargad el instalador de MySQL y procurad instalar lo que os interese (con el servidor ya basta).

Podéis optar por escoger la instalación por defecto para desarrolladores (os instalará cosas que no necesita un desarrollador PHP habitualmente), o bien podéis instalar los paquetes que queráis manualmente.

En el caso de mi instalación escojo únicamente el servidor de MySQL en su versión x64.

Y para instalar, “siguiente, siguiente, siguiente”…

Enlaces útiles y referencias

Me gustaría enlazar a las referencias pero no puedo ya que hace demasiado tiempo que ando haciendo todo este proceso de memoria y no recuerdo exactamente de dónde saqué la información. Sé que al final lo hice funcionar gracias a alguien que explicaba cómo correr FastCGI en Linux, pero ni de coña lograría encontrar el enlace original.

A parte de esto (que lo digo en parte para que seáis conscientes de que todo se puede encontrar buscándolo), os adjunto los ficheros de configuración resultantes tras la configuración explicada en este tutorial.

Si has trasteado o estás trasteando con Laravel 4 habrás notado que está algo verde. Entre varias cosas se echa en falta que no tenga una manera sencilla para poder ordenar los resultados por columnas.

Dado que yo vengo de CakePHP, me he inspirado en él para crear el sistema de ordenación por columnas, así que si en Cake podemos crear un enlace para ordenar así:

1
2
3
4
5
<tr>
  <th><?php echo $this->Paginator->sort('id') ?></th>
  <th><?php echo $this->Paginator->sort('name', 'Nom') ?></th>
  <th><?php echo $this->Paginator->sort('address', 'Adreça') ?></th>
</tr>

En este tutorial verás cómo hacer para poder ordenar así, utilizando blade (el sistema de plantillas por defecto de Laravel 4):

1
2
3
4
5
<tr>
    <th>{{ $posts->sort('id') }}</th>
    <th>{{ $posts->sort('name', 'Nom') }}</th>
    <th>{{ $posts->sort('address', 'Adreça') }}</th>
</tr>

laravel_paginate

Para poder hacer esto tendrás que extender el paginador de Laravel para añadirle las funciones que necesites (como sort).

Dado que lo que es ordenar en sí no tiene ninguna complicación y lo puedes encontrar documentado, en este tutorial me centraré más en cómo extender classes de Laravel 4 para adaptarlo a tus necesidades y de paso verás cómo tener la ordenación por columnas bien organizada (a nivel código) para mejor reutilización en futuros proyectos.

Nota: Debido al continuo desarrollo de Laravel este tutorial ha quedado rápidamente desfasado. He actualizado todos los enlaces para que funcionen correctamente pero ten en cuenta que algunos de estos ficheros han cambiado mucho o directamente ya no existen.

Antes de empezar me gustaría aclarar que no soy experto en Laravel ni mucho menos, simplemente me puse a trastear con él, vi que no tenía esta funcionalidad y exploré hasta encontrar la manera de integrarlo mejor en mi aplicación. Gracias a ello aprendí mucho mejor cómo funciona Laravel (internamente) y es en parte esto lo que quiero compartir contigo.

Por otro lado, a pesar de que el siguiente post rebosa de referencias externas a documentación, código incrustado y código en github, no estaría de más que hubieras leído la documentación de Laravel 4 antes de empezar.

Entendiendo el paginador

La Facade Paginator

Nota: Si no sabes qué es una Facade, repásate la documentación.

Para hacer todo esto lo primero que hay que hacer es entender cómo funciona el paginador actual de Laravel 4. Vayamos al grano.

Si accedes al directorio vendor/laravel/framework/src/Illuminate/Pagination verás que hay un directorio llamado views, un fichero composer.json y cuatro ficheros php.

En la carpeta views hay tres plantillas para mostrar los botones de la paginación. Si no leíste la documentación de Laravel sobre la paginación, gracias a este directorio acabas de averiguar que los botones de paginación tienen plantillas y que hay tres definidas por defecto.

El fichero composer.json ignóralo y de los ficheros PHP, fíjate en los tres siguientes:

El fichero Paginator.php es el que realmente te interesa ya que aquí es donde residen métodos como links() y por tanto es donde te interesa añadir el método sort().

No obstante, para poder hacer todo esto posible necesitarás hacer tu propio PaginationServiceProvider.php y tu propio Environment.php, ya que es en esos dos ficheros donde se inicializa el paginador.

Para entender esto mejor, abre tu fichero app.php y en el array de providers fíjate que, entre varios providers, hay el siguiente:

1
2
3
4
5
6
7
8
9
10
11
return array(
  // ... otros parámetros de configuración ...

  'providers' => array(
    // ... otros providers ...
    'Illuminate\Pagination\PaginationServiceProvider',
    // ... más providers ...
  )

  // ... más parámetros de configuración ...
);

Así pues, para cargar el paginador lo primero que hace Laravel es cargar la clase PaginationServiceProvider.

Si abres este fichero podrás observar cómo en el método register se inicializa la clase Environment.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
 * Register the service provider.
 *
 * @return void
 */
public function register()
{
  $this->app['paginator'] = $this->app->share(function($app) {
    $paginator = new Environment($app['request'], $app['view'], $app['translator']);

    $paginator->setViewName($app['config']['view.pagination']);

    return $paginator;
  });
}

Y en la clase Enviornment, finalmente, en el método make() se innicializa la clase Paginator.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * Get a new paginator instance.
 *
 * @param  array  $items
 * @param  int    $total
 * @param  int    $perPage
 * @return \Illuminate\Pagination\Paginator
 */
public function make(array $items, $total, $perPage)
{
  $paginator = new Paginator($this, $items, $total, $perPage);

  return $paginator->setupPaginationContext();
}

Resumiendo: En app.php se carga el PaginationServiceProvider que carga Environment que a su vez carga Paginator; así que habrá que crear tres clases para poder crear el método sort().

El método paginate de Eloquent

Esto que has visto únicamente es para añadir el método sort(), que básicamente pintará enlaces en las vistas, pero todavía tienes que hacer que la paginación te haga caso y ordene según los parámetros establecidos.

Si recuerdas la documentación de Laravel, para hacer la paginación de elementos debes hacer algo así (los ejemplos a continuación son de un controlador):

1
2
3
4
5
6
public function index()
{
  $posts = Post::paginate(20);

  return View::make('posts.admin.index')->with('posts', $posts);
}

Éste método paginate() forma parte del Builder de Eloquent, así que también habrá que extenderlo si quieres que la paginación tome parámetros adicionales de la URL para ordenar los resultados.

Si en lugar de paginar como en el ejemplo anterior lo hubiera hecho así:

1
2
3
4
5
6
public function index()
{
  $posts = Db::table('posts')->paginate('15');

  return View::make('posts.admin.index')->with('posts', $posts);
}

En vez de modificar el Builder de Eloquent tendrías que modificar el Builder de queries. Aun así, en este tutorial me centraré en el método paginate() del Builder de Eloquent (primer ejemplo).

No obstante, el Builder de Eloquent no se carga igual que el paginador. Éste está inyectado en los modelos, por lo que podrías modificar el método paginate directamente redefiniéndolo en el modelo deseado, ya que éste extiende de Eloquent:

1
2
3
4
5
6
7
8
// app/models/Post.php
class Post extends Eloquent
{
  public function paginate($perPage, $columns = array('*'), $orderBy = array())
  {
    // nuestros cambios para ordenar por columna
  }
}

Fácilmente puedes añadirlo en un nuevo modelo padre del cual extender, por ejemplo AppModel (ya que digo que vengo de CakePHP, voy a demostrarlo):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// app/models/AppModel.php
class AppModel extends Eloquent
{
  public function paginate($perPage, $columns = array('*'), $orderBy = array())
  {
    // nuestros cambios para ordenar por columna
  }
}

// app/models/Post.php
class Post extends AppModel
{

}

Pero esto en realidad no sería muy reusable, así que en su lugar, y ya que Laravel está pensado para ser utilizado con PHP >= 5.4, lo que harás será crear un trait.

Habiendo creado el trait simplemente habrá que indicarlo en aquellos modelos donde quieras utilizarlo:

1
2
3
4
class Post extends Eloquent
{
  use OrderBy; // donde OrderBy es el nombre que has puesto al trait
}

Los cambios

Confiando en que has entendido algo de lo que he explicado hasta ahora, resumiré los cambios a hacer y enlazaré hacia los ficheros que he publicado en github.

Yo tengo todas mis extensiones dentro del directorio app/library/Minombre/Extensions, pero tienes que poder ponerlas en cualquier directorio que cargue clases Laravel siempre y cuando respetes los namespaces.

Yendo al grano

Si quieres ahorrarte el proceso que viene a continuación puedes añadir mi proyecto como un submódulo de tu proyecto fácilmente:

1
git submodule add https://github.com/elboletaire/laravel-utils-and-extensions.git app/library/Elboletaire

Si no utilizas git descarga directamente el zip de github y descomprime los contenidos del fichero en un nuevo directorio app/library/Elboletaire.

El método sort

Volviendo al resumen, tienes que crear tu propio PaginationServiceProvider para en él cargar un nuevo Environment que finalmente será quien cargue el Paginator.

Es en ésta última clase Paginator donde deberás definir el método sort y otros métodos necesarios para que éste funcione correctamente. Como digo muy a menudo, el código habla por sí solo, así que no me centraré en explicarlo.

Una vez has creado tus ficheros (con su contenido, evidentemente) tienes que modificar el array de providers para que cargue tu PaginationServiceProvider en lugar de el de Laravel:

1
2
3
4
5
6
7
8
9
10
11
12
return array(
  // [...]

  'providers' => array(
    // reemplazas el original
    // 'Illuminate\Pagination\PaginationServiceProvider',
    // por el tuyo
    'Elboletaire\Extensions\Pagination\PaginationServiceProvider'
  )

  // [...]
);

El método paginate

Crea un trait en el mismo directorio donde has creado los ficheros anteriores; éste será el encaragdo de modificar la consulta hacia la base de datos a partir de los datos recibidos vía GET.

Puedes ponerle el nombre que quieras, al mío lo he llamado PaginatorSort, pero como digo es cuestión de gustos.

Una vez creado y hecha la lógica sólo hay que llamarlo desde el modelo que quieras que tenga ordenación por columnas:

1
2
3
4
class Post extends Eloquent
{
  use Elboletaire\Extensions\Pagination\PaginatorSort;
}

Añadiendo paginación

Hechos estos dos sencillos pasos, ya puedes añadir a tus vistas los enlaces para poder ordenar por columna:

1
2
3
4
<tr>
    <th>{{ $posts->sort('id') }}</th>
    <th>{{ $posts->sort('name', 'Nombre') }}</th>
</tr>

Y mientras tu controlador (o ruta) cargue los datos de la paginación mediante el modelo (al que previamente has cargado el trait creado):

1
2
3
4
5
6
public function index()
{
  $posts = Post::paginate(20);

  return View::make('posts.admin.index')->with('posts', $posts);
}

…la paginación estaría funcionando. Congrats my friend :D

Si quieres puedes especificar el orden por defecto pasándolo como tercer parámetro:

1
2
3
4
5
6
7
8
9
10
11
public function index()
{
  $posts = Post::paginate(20, null, 'name asc');
  // o bien pasado como array:
  $posts = Post::paginate(20, null, array(
    'direction' => 'asc',
    'sort'      => 'name'
  ));

  return View::make('posts.admin.index')->with('posts', $posts);
}

Fin [referencias y esas cosas]

Si eres lector asiduo (aunque aquí no nos vaya mucho esto de la asiduidad..) sabrás que estoy abierto a que me preguntéis lo que sea —otra cosa es si sabré contestar…

El repositorio está colgado en github, entre otras cosas, para que la gente colabore; así que si te animas ya sabes.

Más información y fuentes:

Hoy me he entretenido en crear un tema para zsh inspirado en af-magic pero añadiendo los features de posh-git, mostrando información detallada sobre el estado actual de git (ficheros modificados, creados, etc).

oh my zsh racotecnic theme

Muestra los datos exactamente igual que lo hace posh-git, salvo por que me he comido los ceros (no me parecen necesarios) y le he hecho un pequeño añadido: en caso de tener cambios stashed mostrará un asterisco en el lateral derecho:

git stash racotecnic zsh theme

Para instalarlo simplemente tenéis que descargarlo en vuestro directorio .oh-my-zsh/themes y modificar la variable ZSH_THEME para cargar el template:

1
ZSH_THEME='racotecnic'

Guardáis el fichero .zshrc y lo recargáis para que surta efecto:

1
source ~/.zshrc

Ea :)

PD. No dudéis en colaborar en github o comentármelo por aquí si se os ocurre cualquier mejora.

Posh-git mola pero si, como yo, sois algo amantes de linux y encima habéis probado zsh sabréis que zsh mola mucho más.

zsh under windows using cygwin

Podemos instalar zsh en windows utilizando cygwin de manera bastante sencilla y con un par de cambios en el registro de windows tendremos un enlace en el menú contextual para abrir zsh en cualquier directorio rápidamente.

obre zsh aquí

Antes de empezar tenéis que tener en cuenta lo siguiente: a la hora de crear el enlace del menú contextual hay que crear dos entradas en el registro de windows, una para cuando hacemos clic derecho sobre un directorio seleccionado (accederemos a dicho directorio) y la otra para cuando hacemos clic derecho sin tener un directorio seleccionado (accederemos al directorio actual).

¿Porqué os digo esto? Porque para poder crear la primera entrada en el registro (hacer clic derecho sobre un directorio seleccionado) sólo podemos hacerlo si instalamos la versión de cygwin de 32 bits ya que no hay manera de hacerlo si no es con el paquete chere.

Es decir que si no queréis dicha opción del menú contextual podéis optar por descargar la versión de 64 bits en lugar de la de 32, aunque no os lo recomiendo ya que yo no he logrado hacer funcionar del todo bien lo del menú contextual en dicha versión (y el tutorial está enfocado a la versión de 32 bits, vamos que me limpio las manos con la de 64). Chere ya está disponible para 64 bits así que no tendréis problema si lo hacéis con el instalador de x64.

Descargad cygwin e instalad con normalidad (siguiente, siguiente…) hasta que lleguéis al listado de paquetes a instalar.

cygwin packages

Cuando lleguéis al listado utilizad el buscador y marcad para descargar los siguientes paquetes:

  • chere necesario para poder abrir la ubicación del directorio seleccionado. Ubicado en «Shells». No es necesario que la instaléis si no queréis abrir el menú contextual al hacer clic derecho sobre un directorio.
  • zsh ubicado en «Shells».
  • curl o wget cualquiera nos servirá para instalar oh-my-zsh. Ambos ubicados en «Web».
  • git necesario para instalar oh-my-zsh. Ubicado en «Devel».
  • ncurses necesario desde cygwin 1.7, ya que no viene incluido con el sistema base. Este paquete tiene ejecutables de sistema necesarios por el instalador de oh-my-zsh.

Y terminad la instalación (siguiente, siguiente…).

Ahora vamos a instalar oh-my-zsh. Para ello abrid una consola de cygwin. Si durante la instalación desmarcasteis el checkbox para añadir el acceso directo podéis ejecutar la consola accediendo a la carpeta de instalación de cygwin y ejecutando el fichero Cygwin.bat.

bash init

En la consola ejecutad lo siguiente:

1
2
3
4
# Si hemos instalado curl
curl -L https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh | sh
# Si hemos instalado wget
wget --no-check-certificate https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O - | sh

Esto instalará oh-my-zsh en vuestro directorio $HOME de cygwin (exactamente igual que en linux).

Ahora que tenemos oh-my-zsh instalado pondremos zsh como la consola por defecto para nuestro usuario.

Para ello hay que modificar el fichero /etc/passwd. Podéis hacerlo con vuestro editor favorito (a.k.a. sublime text, geany, notepad++…) accediendo al directorio de instalación de cygwin/etc y modificando el fichero passwd o si lo preferís podéis hacerlo mediante la propia consola de cygwin, utilizando el editor vi:

1
vi /etc/passwd

Buscáis vuestro usuario y al final de la línea substituís /bin/bash por /bin/zsh .

Si ejecutáis de nuevo el terminal de cygwin debería mostraros una consola de zsh como esta:

zsh-init

Ojo: Si habéis ejecutado la consola utilizando Cygwin.bat tendréis que modificar dicho fichero antes de ejecutarlo para modificar ahí el terminal por defecto, ya que éste fichero ignora la configuración en /etc/passwd. Para ello, editadlo y reemplazad bash por zsh, dejándolo así:

1
2
3
4
5
6
@echo off

C:
chdir C:\cygwin\bin

zsh --login -i

Ya tenemos zsh instalado y corriendo como si de una instalación de linux se tratara. Podemos modificar el fichero .zshrc para cambiar opciones como el tema o los plugins que zsh debería cargar al iniciar. A mi me gusta especialmente el tema af-magic racotecnic:

oh my zsh racotecnic theme

Bien, ahora que tenemos el terminal configurado es hora de añadir el acceso directo en nuestro menú contextual.

Si habéis instalado cygwin en la raíz de C:\ podéis descargar este fichero para instalar rápidamente el acceso directo del menú contextual. Ojo que está en catalán así que si queréis cambiarlo tendréis que abrirlo con un editor de texto y cambiar el «Obre zsh» por «Abrir zsh».

Si queréis ver paso por paso cómo he creado el acceso directo en el menú contextual seguid los pasos que os describo a continuación. En caso contrario, ya habéis terminado de instalar zsh con oh-my-zsh en windows :D.

Para los curiosos

Ejecutad el editor de registro de windows (windows + R y luego regedit) y desplegad el primer registro HKEY_CLASSES_ROOT.

HKEY_CLASSES_ROOT

En él buscad el subregistro Directory. De aquí nos interesan dos registros: Background y shell. En el primero crearemos el registro para abrir un terminal en el directorio actual y el segundo nos servirá para cuando hagamos clic derecho sobre un directorio (y siempre que éste esté seleccionado).

Desplegad Background y en shell haced clic derecho / Nuevo / Clave y le ponéis un nombre como minTTYzsh. En la columna de la derecha hacéis doble clic sobre (Predeterminado) y escribís el texto que queráis mostrar en el menú contextual, yo tengo puesto «Obre zsh».

minTTYzsh key

Si queréis añadirle un icono a la opción del menú desplegable, en la columna de la derecha hacéis clic derecho / Nuevo / Valor de cadena y como nombre le ponéis «Icon». Hacéis doble clic sobre él e indicáis la ruta hacia un icono o un ejecutable (.exe) que contenga iconos. Para indicar el índice de un icono (en caso de que el .ico o el .exe tenga múltiples iconos) tenéis que indicarlo separado de una coma: C:\\cygwin\\cygwin.ico,1.

En nuestro caso indicaremos el icono de mintty, ya que es el ejecutable que utilizaremos como consola: C:\\cygwin\\bin\\mintty.exe

mintty icon

Haced clic derecho en el escritorio y podréis comprobar cómo aparece ya la opción creada en el menú contextual. Pero dicha opción no tiene comando asociado, así que vamos a creárselo.

En la columa de la izquiera del editor de registro de windows hacéis clic derecho sobre la clave creada anteriormente minTTYzsh y seleccionáis Nuevo / Clave y le ponéis como nombre command.

Después de hacer esto hacemos doble clic en el nombre de la columna de la derecha, que aparecerá como (Predeterminado). En el editor que nos aparecerá introducimos lo siguiente: C:\\cygwin\\bin\\mintty.exe --size=120,40 /bin/env CHERE_INVOKING=1 /bin/zsh -l

minTTYzsh command

Haced clic derecho de nuevo en el escritorio y comprobad que funciona el enlace del menú contextual. Si habéis seguido los pasos con todo detalle debería de funcionar sin problemas.

obre zsh aquí

Para añadir la entrada de registro que nos permitirá abrir la consola en un directorio seleccionado seguid los pasos de antes pero en lugar de crear la clave minTTYzsh en Directory/Background/shell deberéis crearla directamente en Directory/shell y en el momento de indicar el comando a ejecutar tendréis que introducir lo siguiente:

C:\\cygwin\\bin\\mintty.exe --size=120,40 /bin/env /bin/xhere /bin/zsh '%L'

En el caso anterior también podríamos haber iniciado la consola indicando /bin/xhere pero tengo la sensación que del otro modo es algo más rápido al cargar (notaréis que en este caso al ejecutar el terminal empieza con «Starting /bin/zsh»)

El parámetro --size lo indico para hacer algo más grande el terminal de minTTY ya que por defecto lo encuentro algo pequeño.

En todo caso, ya tenéis zsh con oh-my-zsh funcionando bajo windows y con la opción en los menús contextuales :D

Update a 10 de enero de 2014: Acabo de encontrar en github un script en bash que debería instalar los paquetes necesarios en cygwin y configurarlo para que funcione con zsh. No lo he probado pero no pinta nada mal, podéis echarle un ojo desde aquí: https://github.com/haithembelhaj/oh-my-cygwin.

Update a 15 de febrero de 2014: En windows 8.1 he tenido problemas para lograr tener oh-my-zsh funcionando. El terminal estaba escupiendo un montón de código y me daba el error compdef: unknown command or service: git repetidas veces antes de, finalmente, mostrar el prompt. En este post he encontrado la solución y ha sido tan sencillo como hacer lo siguiente:

1
2
3
4
# accedemos a nuestro directorio home
cd
# listamos el directorio en busca del fichero .zcompdump
ls -lha

Seguramente veáis un fichero llamado .zcompdump-VuestroUsuario. Lo que está sucediendo es que, por motivos que desconozco, zsh va a buscar ese fichero en lugar de el fichero .zcompdump. Ahora que sabemos el nombre del fichero, solucionemos el problema:

1
2
3
4
compinit
# zsh compinit: insecure directories, run compaudit for list.
# Ignore insecure directories and continue [y] or abort compinit [n]? y
cat .zcompdump > .zcompdump-VuestroUsuario

Aseguraros de que .zcompdump-VuestroUsuario coincida exactamente con el fichero que antes habéis visto listado. Cerrad el terminal y volved a abrirlo y debería de estar solucionado.

Más información y fuentes: