jueves, 21 de octubre de 2010

Jugando con Kumbia (2)


Ya tenemos una tabla country en BD, hicimos un modelo, un controlador y unas vistas para realizar sobre ella las operaciones básicas de un CRUD.

Ahora, para ir complicando las cosas, vamos a crear una tabla 'state' para almacenar estados de un país, en la cual vamos a tener una clave foránea a la tabla 'country'. El código SQL de la tabla es el siguiente:

CREATE TABLE state
(
id serial NOT NULL,
state_name character varying(128),
country_id bigint NOT NULL,
CONSTRAINT pk_state PRIMARY KEY (id),
CONSTRAINT fk_country FOREIGN KEY (country_id)
REFERENCES country (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
ALTER TABLE state OWNER TO beisboluser;

Creamos nuestro modelo, controlador y vistas, tal como creamos los propios para 'country'. Cuando lo probamos, e intentamos ingresar una tupla, nos enfrentamos al primer problema: tendríamos que insertar el id del país, lo cual simplemente no sirve. Lo lógico es que aquí contemos con un combo con todos los países y entre ellos seleccionemos el que corresponda.

Para ello, utilizamos el objeto Form::select(), al cual le vamos a pasar el arreglo con los datos que queremos mostrar (en este caso, países), así que primero, debemos ir a nuestro controlador y crear este arreglo. Esta es la función create de nuestro estate_controller.php:

public function create()
{
Load::models('country');
$country = new Country();
$listCountries = $country->getCountries(1);

$this->country_names = array();
foreach ($listCountries->items as $item)
{
$this->country_names[$item->id] = $item->country_name;
}

if(Input::hasPost('state'))
{
$state = new State(Input::post('state'));
if(!$state->save()){
Flash::error('Falló Operación');
}else{
Input::delete();
}
}
}

Lo 'nuevo' son las líneas al comienzo. Cargamos el modelo country y listamos los países. Luego simplemente recorremos la lista de países y vamos almacenando en un arreglo asociativo (así es el arreglo que recibe Form::select()) los ids junto con los nombres de los países.

Ahora veamos nuestro create.phtml:

<div class="content">
<?php View::content(); ?>
<h3>Add State</h3>

<?php echo Form::open(); ?>

<br>
<label>Name
<?php echo Form::text('state.state_name'); ?></label>

<br>
<label>Country
<?php echo Form::select('state.country_id',

$country_names)?></label>

<?php echo Form::submit('Add') ?>

<?php echo Form::close() ?>
</div>

A Form::select() le pasamos como primer parámetro el campo que se almacenará en BD (country_id), y como segundo argumento, el arreglo asociativo (no hace falta decir qué va en el option y qué se muestra en el navegdor).

Si volvemos a probar la interfaz de creación, debe aparecer nuestro combo para seleccionar el país. Insertemos algunos para probar.

El segundo fallo lo encontramos en la acción index. En la tabla, tendremos la lista de estados que hemos ingresado, junto con el id del país correspondiente. Lo lógico, sería que mostraramos el nombre del país y no el id.

Si hicimos nuestro 'state' tal como 'country' o como en el ejemplo Beta2 CRUD, el modelo tendrá sólo una función getStates(), que internamente utiliza paginate() para listar los elementos que se encuentran en la tabla state, mostrando por página la cantidad de tuplas que especifiquemos. El problema es que en esta tabla sólo tenemos el id del país y no el nombre: necesitamos hacer una unión con la tabla 'country'. Lo que haremos será crear otra función en el modelo, que llamaremos getStatesWithCountry, cuyo código es el siguiente:

public function getStatesWithCountry($page, $ppage=1000)
{
$sqlquery = "SELECT * FROM state INNER JOIN country ON (state.country_id = country.id)";
return $this->paginate_by_sql($sqlquery, "page: $page", "per_page: $ppage");
}

Lo que hicimos fue crear una cadena con nuestra consulta y pasársela como argumento a la función paginate_by_sql(), propia de Active Record. Luego, debemos modificar nuestro index, para que utilice getStatesWithCountry() y no getStates():

public function index($page=1)
{
$estate = new Estate();
$this->listStates = $estate->getStatesWithCountry($page);
}

Y lógicamente, modificaremos la vista index.phtml para que utilice el campo country_name, en vez de country_id:

<div class="content">
<?php echo View::content(); ?>
<h3>States</h3>

<?php echo Html::linkAction("create", 'Add') ?>

<table>
<col class="first-column" />
<col span="3" />
<col class="last-column" />
<tr class="first-row">
<th>Name</th>
<th>Country</th>
<th></th>
<th></th>
</tr>
<?php foreach ($listEstates->items as $item) : ?>
<tr>
<td><?php echo $item->estate_name ?></td>
<td><?php echo $item->country_name ?></td>
<td><?php echo Html::linkAction("edit/$item->id/", 'Edit') ?></td>
<td><?php echo Html::linkAction("del/$item->id/", 'Delete') ?></td>
</tr>
<?php endforeach; ?>
</table>

</div>


Y eso es todo, nuestra vista quedará funcionando. En este caso, utilizamos la función paginate_by_sql(), la cual retorna el mismo objeto que paginate(), pero ajustado a la sentencia SQL que ejecutemos. Esta es sólo una de las posibilidades que ofrece kumbia. También podemos utilizar la función find() y la función find_all_by_sql().

Kumbia recomienda evitar utilizar código SQL directamente. Yo no le encuentro ningún problema, de hecho, me agrada construir mis propias consultas SQL. Cuestión de gustos.

La función find_all_by_sql() también recibe la consulta SQL, pero no realiza paginado, es decir, que retorna en un arreglo todos los elementos consultados. La función sería así:

public function getEstatesWithCountry()
{
$sqlquery = "SELECT * FROM state INNER JOIN country ON (state.country_id = country.id)";
return $this->find_all_by_sql($sqlquery);
}

Y si utilizamos esta opción, tendríamos que modificar la vista, pues ahora no tenemos un objeto Page, sino un simple arreglo. Sólo necesitamos hacer un cambio en el foreach, que quedaría así:

<?php foreach ($listStates as $item) : ?>


Dejo la entrada hasta aquí. En el siguiente post seguiré introduciendo algunos otros conceptos de kumbia a medida que voy construyendo mi aplicación. Buenas noches


0 comentarios:

Publicar un comentario