Kohana es un marco PHP 5 que utiliza el patrón arquitectónico Model View Controller (MVC). Hay varias razones por las que debes elegir Kohana, pero las principales son la seguridad, la ingravidez y la simplicidad. En este tutorial, presentaré sus características principales y, con una simple demostración, te mostraré cuánto tiempo puede ahorrar Kohana..
Kohana es un marco PHP5 que utiliza el patrón arquitectónico Model View Controller. MVC mantiene la lógica de la aplicación.
aparte de la presentación. Esto nos permite crear un código más limpio y ahorrar tiempo para la búsqueda de errores.
Al no estar familiarizado con este patrón:
Kohana fue originalmente una bifurcación de CodeIgniter (CI), que es un producto de código abierto de EllisLab. Hay muchas similitudes entre CI y Kohana, pero todo el código es nuevo o está completamente reescrito. Como puedes leer en el sitio web oficial de Kohana, las principales características son:
Empecemos. Visite el sitio web oficial de Kohana http://kohanaphp.com y haga clic en el cuadro verde en la esquina derecha para descargar la última versión. Todas las bibliotecas, ayudantes y vistas de Kohana están incluidas en el paquete de descarga predeterminado, pero puede seleccionar módulos adicionales, herramientas de proveedores e idiomas si lo desea. Para los fines de este tutorial, el paquete predeterminado puede ser suficiente. Haga clic en "Descargar Kohana!" para comenzar la descarga.
Una vez que hayas terminado de descargarlo:
$ config ['site_domain'] = 'localhost / kohana';
Kohana se ejecutará en casi cualquier entorno con una configuración mínima. Hay algunos requisitos mínimos de servidor:
También hay algunas extensiones requeridas..
Si su instalación se completa con éxito, será redirigido a esta página de prueba:
Si alguna de las pruebas falla, debe corregirlas antes de seguir adelante.
Si todas las pruebas han pasado, vaya al directorio de Kohana y elimine o cambie el nombre del script install.php. Actualiza, y verás una página de bienvenida como esta:
Kohana está lista para irse. No se necesita ninguna otra configuración. Este marco es increíble. ¿No es así? Repasemos algún código. Sígueme.
Los tutoriales de programación canónica comienzan con el ejemplo "Hola mundo". Creo, en cambio, que una aplicación simple puede darte una idea clara de cómo funciona el marco. Entonces, construiremos un administrador de colección de CD, solo para una demostración divertida. Antes de comenzar la codificación, se requiere una breve introducción al sistema de archivos Kohana..
Nuestra aplicación se colocará en el solicitud carpeta. En esta carpeta hay varias subcarpetas, pero necesitamos lo siguiente para nuestro proyecto:
Las subcarpetas restantes no son necesarias para este tutorial, por lo que lo invito a aprender más en el sitio web de Kohana.
los sistema Las carpetas alojan el núcleo de Kohana y las herramientas de Kohana, como bibliotecas, ayudantes y archivos de configuración predefinidos. En este proyecto usaremos algunas bibliotecas y algunos ayudantes: buenas herramientas para acelerar su trabajo.
los bienes carpeta no es una carpeta predefinida de Kohana. Lo he creado para archivos multimedia como CSS, JS e imágenes. Te mostraré cómo incluir estos archivos en el proyecto..
los módulos La carpeta es el lugar para colocar colecciones reutilizables de archivos relacionados que, en conjunto, agregan una funcionalidad particular a una aplicación. El módulo de autenticación, proporcionado por el equipo de Kohana, es un ejemplo de módulo.
Esta es una introducción muy breve al sistema de archivos Kohana, pero es suficiente para los propósitos de este tutorial. No quiero aburrirte más con la teoría..
He elegido MySQL como mi DBMS, pero recuerde que Kohana también es compatible con MsSQL, MySQLi, PostgreSQL, PDOSqlite. Cree una base de datos llamada "cd_collection" o elija el nombre que prefiera, y ejecute el siguiente SQL a través de phpMyAdmin o cualquier herramienta para manejar la administración de MySQL.
CREAR TABLA 'álbumes' ('id' int (11) NOT NULL auto_increment, 'nombre' varchar (50) clasificar utf8_bin NOT NULL, 'author' varchar (50) clasificar utf8_bin NOT NULL, 'genre_id' int (11) NOT NULL , CLAVE PRIMARIA ('id'), CLAVE 'genre_id' ('genre_id')) MOTOR = InnoDB CHARLA DE PREDETERMINADA = utf8 COLLATE = utf8_bin AUTO_INCREMENT = 19; INSERT INTO 'albums' ('id', 'name', 'author', 'genre_id') VALUES (2, 'Lines, Vines And Trying Times', 'Jonas Brothers', 16), (3, 'The END' , 'The Black Eyed Peas', 16), (4, 'Relapse', 'Eminem', 18), (5, 'Monuments And Melodies', 'Incubus', 1), (6, 'Thriller', 'Michael Jackson ', 16), (7,' Back in Black ',' AC / DC ', 4), (8,' The Dark Side of the Moon ',' Pink Floyd ', 4), (9,' Bat out of Hell ',' Meat Loaf ', 4), (10,' Backstreet Boys ',' Millennium ', 16), (11,' Rumours ',' Fleetwood Mac ', 4), (12,' Come on Over ' , 'Shania Twain', 16), (13, 'Led Zeppelin IV', 'Led Zeppelin', 4), (14, 'Jagged Little Pill', 'Alanis Morissette', 4), (15, 'Sgt. Pepper "Lonely Hearts Club Band ',' The Beatles ', 16), (16,' Falling into You ',' C√ © line Dion ', 16), (17,' Music Box ',' Mariah Carey ', 16 ), (18, 'Born in the USA', 'Bruce Springsteen', 4); CREATE TABLE 'genres' ('id' int (11) NOT NULL auto_increment, 'name' varchar (50) collate utf8_bin NOT NULL, PRIMARY TECLA ('id'), TECLA ÚNICA 'nombre' ('nombre')) MOTOR = I nnoDB DEFAULT CHARSET = utf8 COLLATE = utf8_bin AUTO_INCREMENT = 22; INSERTAR EN 'genres' ('id', 'nombre') VALORES (1, 'Rock Alternativo'), (2, 'Blues'), (3, 'Clásico'), (4, 'Rock'), (5 , 'País'), (6, 'Danza'), (7, 'Gente'), (8, 'Metal'), (9, 'Hawaiano'), (10, 'Importaciones'), (11, ' Indie Music '), (12,' Jazz '), (13,' Latin '), (14,' New Age '), (15,' Opera '), (16,' Pop '), (17,' Soul '), (18,' Rap '), (20,' Soundtracks '), (21,' World Music '); ALTER TABLE 'albums' ADD RESTRAINT 'genre_inter_relational_constraint' FOREIGN KEY ('genre_id') REFERENCES 'genres' ('id') EN ELIMINAR CASCADE ON UPDATE CASCADE;
Como puede ver, el SQL crea dos tablas, álbumes y géneros, y los rellena con algunos datos. La última instrucción SQL agrega una restricción para la clave externa "genre_id".
La estructura de la base de datos es muy simple y no necesita mucha explicación..
Ahora, tiene que decirle a Kohana dónde se encuentra su base de datos y cómo acceder a ella. Editar el archivo de configuración global. sistema / config / database.php como sigue
$ config ['default'] = array ('benchmark' => TRUE, 'persistent' => FALSE, 'connection' => array ('type' => 'mysql', 'user' => 'root', ' pass '=>' root ',' host '=>' localhost ',' port '=> FALSE,' socket '=> FALSE,' database '=>' cd_collection '),' character_set '=>' utf8 ', 'table_prefix' => ", 'object' => TRUE, 'cache' => FALSE, 'escape' => TRUE);
Este código le dice a Kohana que se conecte a una base de datos MySQL llamada "cd_collection" en localhost con el nombre de usuario "root" y la contraseña "root". Debe cambiar esta configuración de acuerdo con la configuración del servidor de la base de datos..
Ahora vamos a crear nuestro primer controlador. Recuerda estas convenciones.
Además, recuerde cómo Kohana estructura sus URL y cómo puede llamar a un método de controlador; por ejemplo, http: //hostname/kohana_directory/index.php/controller/function.
Echemos un vistazo a este sencillo controlador..
PHP5 OOP es un requisito previo. Así que si no estás bien versado, puedes aprender más aquí.
La función constructora, llamada __construct, inicializa la clase y llama al constructor principal.
La función de índice es la función predeterminada, por lo que se llamará si llamamos al controlador sin
especificando cualquier función (por ejemplo, http: //localhost/index.php/kohana/album. Después del controlador de nombre
no hay ninguna función, se llamará la función de índice por defecto.)Dadas estas reglas básicas, concentrémonos en nuestra aplicación. El controlador de álbum implementa todas las acciones para
La gestión de la colección de álbumes. Este controlador nos permite crear un nuevo álbum, para mostrar los álbumes almacenados en nuestra base de datos,
para actualizar un álbum, y para eliminar un álbum.Entonces, cambiemos la clase de la siguiente manera.
Crear un archivo llamado album.php en aplicación / controladores / y pega lo siguiente.
album_model = nuevo Album_Model; $ this-> genre_model = new Genre_Model; $ this-> list_view = new View ('list'); $ this-> update_view = new View ('update'); $ this-> create_view = new View ('create'); índice de función pública () $ this-> show_albums_list (); función privada show_albums_list () $ albums_list = $ this-> album_model-> get_list (); $ this-> list_view-> set ('albums_list', $ albums_list); $ this-> list_view-> render (TRUE); función pública show_create_editor () $ this-> create_view-> set ('genres_list', $ this-> get_genres_list ()); $ this-> create_view-> render (TRUE); función pública show_update_editor ($ id) $ album_data = $ this-> album_model-> read ($ id); $ this-> update_view-> set ('album_id', $ album_data [0] -> id); $ this-> update_view-> set ('name', $ album_data [0] -> name); $ this-> update_view-> set ('author', $ album_data [0] -> author); $ this-> update_view-> set ('genre_id', $ album_data [0] -> genre_id); $ this-> update_view-> set ('genres_list', $ this-> get_genres_list ()); $ this-> update_view-> render (TRUE); public function create () $ album_data = array ('name' => $ this-> input-> post ('name'), 'author' => $ this-> input-> post ('author'), 'genre_id' => $ this-> input-> post ('genre_id')); $ this-> album_model-> create ($ album_data); url :: redirect ('álbum'); actualización de la función pública () $ album_data = array ('name' => $ this-> input-> post ('name'), 'author' => $ this-> input-> post ('author'), 'genre_id' => $ this-> input-> post ('genre_id')); $ this-> album_model-> update ($ this-> input-> post ('album_id'), $ album_data); url :: redirect ('álbum'); eliminar función pública ($ id) $ this-> album_model-> delete ($ id); url :: redirect ('álbum'); función privada get_genres_list () $ db_genres_list = $ this-> genre_model-> get_list (); $ genres_list = array (); if (sizeof ($ db_genres_list)> = 1) foreach ($ db_genres_list como $ item) $ genres_list [$ item-> id] = $ item-> name; devolver $ genres_list;Déjame explicarte lo que hace este código.
Las variables de cinco miembros se declaran en la parte superior de la clase:
privado $ album_model; privado $ genre_model; privado $ list_view; privado $ create_view; privado $ update_view;Estos miembros son privados porque quiero limitar la visibilidad solo a esta clase.
En el método de construcción, el modelo y los objetos de vista se crean utilizando los cinco miembros:
$ this-> album_model = new Album_Model; $ this-> genre_model = new Genre_Model; $ this-> list_view = new View ('list'); $ this-> update_view = new View ('update'); $ this-> create_view = new View ('create');Para crear un objeto modelo usa esta sintaxis:
$ obj_name = nuevo Name_Model;Para crear un objeto de vista, use esta sintaxis:
$ obj_name = nueva vista ('view_filename_without_extension');Ahora hay dos objetos para acceder al modelo de álbum y género, y tres objetos para acceder a las vistas necesarias para renderizar la presentación.
El método de índice llama al método show_albums_list que lista todos los álbumes almacenados en la base de datos.
$ albums_list = $ this-> album_model-> get_list (); $ this-> list_view-> set ('albums_list', $ albums_list); $ this-> list_view-> render (TRUE);En este método, puede ver cómo se utilizan el modelo y el objeto de vista para acceder a métodos relativos. "get_list" es un método modelo (lo veremos más adelante) que devuelve todos los álbumes almacenados en la base de datos. El resultado se guarda en la matriz "$ album_list". Para pasar la matriz de resultados del controlador a la vista, se llama al método "set" en el objeto de vista. Este método requiere dos parámetros: una nueva variable vacía (lista de álbumes) para contener los datos de una variable existente ($ lista de álbumes). Ahora, la nueva variable "album_list" contiene la matriz $ album_list (veremos más adelante cómo mostrar el contenido en la vista). El método "render", con el parámetro TRUE, es necesario para enviar datos al navegador.
El método show_create_editor muestra la interfaz de usuario para insertar un nuevo álbum.
$ this-> create_view-> set ('genres_list', $ this-> get_genres_list ()); $ this-> create_view-> render (TRUE);La lista de los géneros se pasa a la vista..
El método show_update_editor muestra la interfaz de usuario para actualizar un álbum existente.
$ album_data = $ this-> album_model-> read ($ id); $ this-> update_view-> set ('album_id', $ album_data [0] -> id); $ this-> update_view-> set ('name', $ album_data [0] -> name); $ this-> update_view-> set ('author', $ album_data [0] -> author); $ this-> update_view-> set ('genre_id', $ album_data [0] -> genre_id); $ this-> update_view-> set ('genres_list', $ this-> get_genres_list ()); $ this-> update_view-> render (TRUE);"leer" es un método modelo (lo veremos más adelante) que devuelve datos ($ album_data) del álbum con una id igual a $ id. Luego, cada elemento del álbum de datos devuelto se pasa a la vista.
El método de creación recibe datos, para un nuevo álbum, desde la vista y los datos se almacenan en la base de datos.
$ album_data = array ('name' => $ this-> input-> post ('name'), 'author' => $ this-> input-> post ('author'), 'genre_id' => $ this -> input-> post ('genre_id')); $ this-> album_model-> create ($ album_data); url :: redirect ('álbum');$ album_data es una matriz que contiene los datos POST de la vista. Para guardar el álbum, la matriz se pasa al método crear modelo. La última línea es una llamada a un método auxiliar. Los ayudantes son simplemente funciones que le ayudan con el desarrollo. Las clases de ayuda se cargan automáticamente por el marco. Los ayudantes se declaran como métodos estáticos de una clase, por lo que no es necesario crear una instancia de la clase. En este caso, se llama al método "redireccionar" del ayudante "url" y le dice a Kohana que redirija el navegador al controlador del álbum. Esto evita una nueva inserción (por ejemplo presionando F5).
"Los ayudantes son simplemente funciones que lo ayudan con el desarrollo".
Los métodos de actualización y eliminación funcionan de la misma manera que el método de creación anterior.
El último método get_genres_list obtiene la lista de géneros del modelo ($ db_genres_list) y crea una nueva matriz ($ genres_list) para el cuadro de selección en las vistas.
$ db_genres_list = $ this-> genre_model-> get_list (); $ genres_list = array (); if (sizeof ($ db_genres_list)> = 1) foreach ($ db_genres_list como $ item) $ genres_list [$ item-> id] = $ item-> name; devolver $ genres_list;Paso 8: Crear modelo de proyecto
Ahora creamos modelos para nuestra aplicación web. Recuerda estas convenciones.
Aquí está el código del modelo del álbum. Crear un archivo llamado album.php en aplicación / modelos / y pega el siguiente código en él.
album_table = 'albums'; $ this-> genre_table = 'genres'; lectura de la función pública ($ id) $ this-> db-> where ('id', $ id); $ query = $ this-> db-> get ($ this-> album_table); devuelve $ query-> result_array (); eliminar función pública ($ id) $ this-> db-> delete ($ this-> album_table, array ('id' => $ id)); actualización de la función pública ($ id, $ data) $ this-> db-> update ($ this-> album_table, $ data, array ('id' => $ id)); public function create ($ data) $ this-> db-> insert ($ this-> album_table, $ data); función pública get_list () $ this-> db-> select ('albums.id como id, albums.name como nombre, albums.author como autor, genres.name como género'); $ this-> db-> from ($ this-> album_table); $ this-> db-> join ($ this-> genre_table, 'genres.id', 'albums.genre_id'); $ query = $ this-> db-> get (); devuelve $ query-> result_array ();
Todos los métodos en los modelos hacen uso de la sintaxis del generador de consultas. Esta herramienta de Kohana acelera los tiempos de desarrollo de la base de datos y simplifica la creación de consultas..
Las variables de dos miembros se declaran en la parte superior de la clase:
privado $ album_table; privado $ genre_table;
Estos miembros son privados porque quiero limitar la visibilidad solo a esta clase. Son los contenedores para los nombres de las tablas de la base de datos..
La primera línea en el método del constructor carga la biblioteca de la base de datos Kohana en $ this-> db. En la segunda y tercera líneas se inicializan los dos miembros de la clase..
padre :: __ construir (); $ this-> album_table = 'albums'; $ this-> genre_table = 'genres';
La consulta en el método de lectura recupera los registros del álbum que tienen un determinado identificador ("$ id").
$ this-> db-> where ('id', $ id); $ query = $ this-> db-> get ($ this-> album_table); devuelve $ query-> result_array ();
La consulta en el método de eliminación elimina la fila de la tabla de álbumes que tiene un determinado identificador ("$ id").
$ this-> db-> delete ($ this-> album_table, array ('id' => $ id));
La consulta en el método de actualización actualiza la fila de la tabla de álbumes que tiene un determinado identificador ("$ id") con nuevos valores de la matriz "$ data".
$ this-> db-> update ($ this-> album_table, $ data, array ('id' => $ id));
La matriz "$ data" debe contener nombres de registros como claves de la matriz y valor como valores de la matriz. La matriz "$ data" debe tener esta forma:
$ data = array ('name' => 'album_name', 'author' => 'author_name', 'genre_id' => 'genre_id');
La consulta en el método de creación inserta un nuevo registro con los valores de la matriz "$ data".
$ this-> db-> insert ($ this-> album_table, $ data);
La matriz "$ data" debe tener esta forma:
$ data = array ('id' => 'album_id', 'name' => 'album_name', 'author' => 'author_name', 'genre_id' => 'genre_id');
La consulta en el método get_list recupera todas las filas de álbumes.
$ this-> db-> select ('albums.id as id, albums.name as name, albums.author as author, genres.name as genre'); $ this-> db-> from ($ this-> album_table); $ this-> db-> join ($ this-> genre_table, 'genres.id', 'albums.genre_id'); $ query = $ this-> db-> get (); devuelve $ query-> result_array ();
Ahora, el modelo de género. Crear un archivo llamado genre.php en aplicación / modelos / y pega el código debajo:
genre_table = 'géneros'; function get_list () $ query = $ this-> db-> get ($ this-> genre_table); devuelve $ query-> result_array ();
Este modelo es muy simple, así que no perderé más tiempo para comentarlo. Los modelos y el controlador están listos para funcionar. Trabajemos ahora en las Vistas..
Las vistas son archivos que contienen la capa de presentación para su aplicación. El propósito es mantener esta información separada de la lógica de su aplicación para una reutilización fácil y un código más limpio. Para este proyecto, se requieren tres vistas: una vista para enumerar la colección del álbum, una vista para crear un álbum nuevo y una vista para editar un álbum existente.
Crear un archivo llamado list.php en aplicación / vistas / y pega el siguiente código en:
COLECCIÓN DE CD
Colección de CD | ||||
Nombre del album | Autor | Género | ". $ item-> name." | "; eco "". $ item-> autor." | "; eco "". $ item-> género". | "; eco "".html :: anchor ('album / delete /'.$ item-> id, html :: image (' asset / images / delete.png '))". | "; eco "".html :: anchor ('album / show_update_editor /'.$ item-> id, html :: image (' asset / images / edit.png '))". | "; eco "";?>
Esta vista muestra una página html que contiene una lista de todos los álbumes. Esta lista se ha creado utilizando el bucle foreach que imprime la información en una tabla html. Para cada fila de álbumes, hay dos imágenes: una "cruz roja" y una "cartera". Enlazan respectivamente el método de eliminación del controlador y el método de actualización. Ambos pasan la identificación del álbum al controlador del álbum usando una solicitud de obtención. Encima de la lista hay un botón para crear nuevos álbumes. En este código también utilizamos un ayudante HTML proporcionado por Kohana que acelera las operaciones para escribir páginas HTML..
Ahora vamos a crear un archivo llamado crear.php en aplicación / vistas /.
COLECCIÓN DE CD
Crear nuevo álbum | ".form :: label ('name', 'Name:')." | "; eco "".form :: input ('nombre',") ". | "; eco ""; eco "
".form :: label ('author', 'Author:')." | "; eco "".form :: input ('autor',"). " | "; eco "
".form :: label ('genre', 'Genre:')." | "; eco "".form :: dropdown ('genre_id', $ genres_list)". | "; eco "
".form :: submit ('submit', 'Create album')". | "; eco "
La última, pero no la menos importante, es la vista de actualización. Vamos a crear un archivo llamado update.php en aplicación / vistas /.
COLECCIÓN DE CD
Actualizar album | ".form :: label ('name', 'Name:')." | "; eco "".form :: input ('nombre', $ nombre)". | "; eco ""; eco "
".form :: label ('author', 'Author:')." | "; eco "".form :: input ('author', $ author)." | "; eco "
".form :: label ('genre', 'Genre:')." | "; eco "".form :: dropdown ('genre_id', $ genres_list, $ genre_id)." | "; eco "
".form :: submit ('submit', 'Update album')". | "; eco "
El primero es un editor simple que permite al usuario insertar información sobre un nuevo álbum.
Los campos como autor y nombre se insertarán utilizando una entrada html y el género utilizando una
caja combo. Una vez que el usuario hace clic en el botón crear, se pasa toda la información,
como una solicitud POST, al método de crear / actualizar en el controlador del álbum. Cuando el controlador recibe estos puestos
variables, llama al modelo que inserta un nuevo álbum en la base de datos. Los formularios, en ambos puntos de vista, hacen uso del ayudante de formularios Kohana..
Para darle un poco de estilo a nuestra aplicación, cree el bienes carpeta en la raíz de Kohana en el mismo nivel de la carpeta de la aplicación. Ahora, ábrelo y crea dos nuevas carpetas: css y imagenes.
En el css carpeta crea un nuevo archivo llamado style.css y pega esto:
a font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; fuente-peso: normal; tamaño de fuente: 12px; color: # 00F; vertical-align: text-top; img border: 0; label font-family: Verdana, Ginebra, Arial, Helvetica, sans-serif; fuente-peso: normal; tamaño de fuente: 12px; input border: 1px solid # 000; seleccione ancho: 185px; table.editor text-align: center; Familia tipográfica: Verdana, Ginebra, Arial, Helvetica, sans-serif; fuente-peso: normal; tamaño de fuente: 11px; color: #fff; ancho: 280px; color de fondo: # 666; frontera: 0px; colapso de la frontera: colapso; espaciado de la frontera: 0px; table.editor td.editor_title background-color: # 666; color: #fff; relleno: 4px; alineación de texto: izquierda; font-weight: negrita; tamaño de fuente: 16px; table.editor td padding: 4px; table.list text-align: center; Familia tipográfica: Verdana, Ginebra, Arial, Helvetica, sans-serif; fuente-peso: normal; tamaño de fuente: 11px; color: #fff; ancho: 280px; color de fondo: # 666; frontera: 0px; colapso de la frontera: colapso; espaciado de la frontera: 0px; table.list td.item background-color: #CCC; color: # 000; relleno: 4px; alineación de texto: izquierda; borde: 1px #fff sólido; table.list td.list_title, table.list td.headers background-color: # 666; color: #fff; relleno: 4px; alineación de texto: izquierda; borde inferior: 2px #fff sólido; font-weight: negrita; table.list td.list_title font-size: 16px; table.list td.headers font-size: 12px;
Ahora copia las siguientes imágenes a la imagenes carpeta:
Eso es todo. Apunta tu navegador a http: //localhost/kohana/index.php/album y deberías ver algo similar a esto:
Si intentas crear un nuevo álbum o editar uno existente, deberías ver algo similar a esto:
Por supuesto, se requieren algunas mejoras para esta aplicación, pero con una pequeña cantidad de código, ha creado una pequeña aplicación web.
Ahora, sabe cómo usar el patrón MVC con Kohana y cómo usar las bibliotecas de bases de datos y los ayudantes. Para saber más, lea la documentación oficial..
Gracias a Kohana, el mantenimiento del código es una tarea fácil, y agregar nuevas funciones es muy fácil. Espero que disfrutes este tutorial. Manténgase sintonizado para aprender más sobre Kohana.