Volviendo a PHP

Hace fácil 5 años que no hago nada serio en PHP, empezando desde cero. Mayormente me ha tocado retocar cosas andando o parchar bugs boludos. Sin embargo el otro día tuve que hacer un demo chiquitito en PHP. El principal limitante era el tiempo, por lo que tenía que hacerlo rápido.

Al principio intenté el viejo HTML+PHP all-in-one con ifs $_GET, $_POST, etc. Una locura, no podía avanzar. Mi mente me decía “esto va en el controlador, esto en el view” :D. Borre todo y empecé de nuevo. Pensé dos segundos y dije “tiene que haber algo”. No, CakePHP no es ese “algo”. Era mucha config al pedo, bloated y cada vez que tuve en mi poder un site de Cake para poner en producción terminé a los cabezazos.

Googleando me topé con SlimFramework un micro framework para hacer apps (el Sinatra de CakePHP 😀 ?) y la verdad que dije “ah, mirá, en php también se puede programar como al gente”. No tiene ORM, solo un modo fácil de declarar rutas, con arguments y verbos REST (post, delete, put, etc) y un body para ejecutar la lógica.

Los templates son modulares, podemos llamar a otro .php que genera HTML o usar HAML, Smarty (dios que alguien de de baja este proyecto :P) o armar el propio. Un hola mundo :

get('/hello/:name', function ($name) {
    echo "Hello, $name";
  });
  $app->run();
?>

Simple, no?. La documentación está bastante completa y no tuve mayores drama en sacar andando el demo. Lo único que no me gustaba era que los “views” no podían tener un main template (al menos en la version básica de usar php como template language). Es decir, tenía que poner <? require “header.php”; ?> <? require “footer.php” ?> por todos lados, por lo que decidí implementar me propia “view” :

setTemplate($template);
        extract($this->data);
        ob_start();

        $this->setTemplate("template_pre.php");
        require $this->templatePath;

        $this->setTemplate($template);
        require $this->templatePath;

        $this->setTemplate("template_post.php");
        require $this->templatePath;

        return ob_get_clean();
    }
}
?>

Simplemente hago los 3 render y así tengo view con layout a’la Rails :P.

El segundo punto a resolver era el acceso a la base de datos. Hoy en día laburar con mysql_query, mysql_fetch es inpensable, al menos para prototipado ;). Otra vez, googleando me topé con Idiorm, “A lightweight nearly-zero-configuration object-relational mapper and fluent query builder for PHP5”, o sea, un Arel para PHP, super limiteado pero suficiente por el momento.

Este ORM nos pemite encadenar condiciones y luego obtener objetos, manipularlos y guardarlos de nuevo :

$user = ORM::for_table('user')
    ->where_equal('username', 'j4mie')
    ->find_one();

$user->first_name = 'Jamie';
$user->save();

Si queremos más abstracción todavía el mismo autor implementó Paris, una implementación de ActiveRecord sobre Idiorm. Yo en mi caso no la usé así que lo dejo a criterio del lector probarla.

A modo de ilustrar como quedó mi demo, les comparti el método del index, que obtiene la lista de registros y hace un render del view acorde :

$app->get('/', function () use ($app) {
  $contacts = ORM::for_table('contact')->find_many();
  $app->render('home.php', array('contacts' => $contacts));
});

Y la vista (recortada) :

Contactos

... data['contacts'] as $contact) { ?> ...
nombre ?> <form action="/contacts/id ?>" method="POST" class="form-inline"> <a href="/contacts/id ?>/edit" class="btn">Modificar
Agregar
Anuncios

Edit In Place con Prototype

Una de las cosas que me venían pidiendo en ¡Falta Uno! era que se le pudieran asignar nombre a los equipos de un partido. En un principio pensé solo en poner los campos en el formulario de “crear partido” pero después me pareció que quedaba piola que se puedan editar directamente desde el resumen, usando un “Edit in place”. Me puse a buscar si había algo hecho con Proptotype (que es lo que uso en este proyecto) y encontré esto que viene con varios ejemplos.

La biblioteca es muy fácil de usar y bastante flexible en cómo queremos que se comporte el edit (puede ser un input, un textarea, un combo). Suponiendo que los nombres de equipos estén siendo mostrados de la siguiente manera :

Para poder editar los títulos simplemente basta con ejecutar el siguiente código cuando se carga la página :

$('team_name_1').editInPlace({
  auto_adjust: true,
  select_text: true,
  save_url: ' 1) %>'
});
$('team_name_2').editInPlace({
  auto_adjust: true,
  select_text: true,
  save_url: ' 2) %>'
});

Es recomendable poner este código dentro de un método y usar un Observer para ejecutarlo, así mantenemos separada la lógica JS del HTML. Bastaría con un simple Event.observe(window, 'load', initEIP);.

Por el lado del servidor, el método que atiende el request (el definido el save_url) tiene que devolver el nuevo valor que va a tomar el campo. Por ejemplo, si el nombre no se pudo cambiar, deberíamos devolver el anterior. En este caso que es simple el template a devolver es el siguiente :


Para terminar, en caso de que el navegador del usuario no tenga JS queda el formulario de “Editar/Crear Partido” donde se pueden poner los nombres de manera tradicional.

Multiupload de imágenes con Prototype

Desde hace unos días que estoy haciendo un widget que soporte upload de múltiples archivos para una aplicación web. No fue fácil el comienzo pero despues de varias horas (unas 8 hasta este momento) ya va tomando forma.

Para poder trackear el upload de cada archivo utilizo apache_mod_upload_progress, un genio “Drogomir” :). Para compilarlo en OSX tuve algunos problemas ya que apache2 esta compilado en x86_64 y el default del apsx es x86 pero googleando se encuentra fácil como pasarle el parámetro al gcc. Lo otro que necesitamos tener instalado es mod_rails y apache 2.2. Cuando termine el código y lo publique estará todo explicado en detalle :).

El segundo problema grande fue el formulario. Para hacer el upload lo que hago es crear un iframe oculto y cambiar el target del formulario a ese frame (de esta menera si no tenemos javascript la aplicación degrada automáticamente al upload de imagenes individuales y el usuario no se entera), pero claro, necesitaba tener múltiples input:file, uno por cada archivo a subir. De ponerlos todos juntos tendríamos un POST super gigante que no era lo que se buscaba ya que no podría trackear cada upload por separado.

La solución fue, cada vez que se selecciona un archivo sacar el INPUT del form y guardarlo en un array. El espacio vacío se reemplaza con un nuevo INPUT y como todo es tán rápido, uno no se da cuenta. El problema llegó cuando terminaba el primer archivo, tenía que volver a agregar el siguiente file al formulario y hacer otra vez el submit. Pero si uno llama $(form).submit() desde javascript, el callback onSubmit no es ejecutado (defecto de las implementaciones de todos los navegadores que probé y parece que no va a cambiar) por lo que no era útil.

La solución finalmente fue simular el click enel botón enviar con un simple $(submit_button).click() que resuelve el problema anterior. Les dejo el video para que lo disfruten :).

httpv://www.youtube.com/watch?v=7PqZg_1Pi1w