Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Instalacin
Para instalar Yii solo debe seguir los siguientes 2 pasos:
1. Descargar el framework Yii de yiiframework.com.
2. Descomprimir el archivo a un directorio accesible por el servicio Web.
Tip: Yii no necesita ser instalado en un directorio accesible via web. La
aplicacion Yii tiene un script de entrada la cual usualmente es el nico archivo
que debe ser expuesto a los usuarios Web. Otros scripts PHP , incluidos los de Yii,
pueden (y se recomienda) estar protegidos del acceso Web ya que esos pueden
intentar ser explotado para Hackeo.
1. Requerimiento
Luego de instalar Yii, ustede puede verificar si su server satisface todos los
requerimientos para utilizar Yii. Para hacerlo debe hacer accesible el script de
verificacin de requerimientos para utilizar Yii. Usted puede acceder al script de
verificacin de requerimientos en la siguiente URL en un explorador Web:
http://hostname/path/to/yii/requirements/index.php
El requerimiento mnimo de Yii es que su server soporte PHP 5.1.0 o superior. Yii
ha sido testeado con Apache HTTP server en los sistemas operativos Windows y
Linux. Tambin puede funcionar en otras plataformas que soporten PHP 5.
Creando primera aplicacin Yii
Para ingresar al mundo de Yii, en esta sccin le indicamos como crear nuestra
primera aplicacin Yii. Usaremos la poderosa herramientayiic que puede ser
utilizadapara automatizar la creacin del cdgo de ciertas tareas. Por
conveniencia asumimos que YiiRoot es el directorio donde Yii se encuentra
instalado y WebRoot es la ruta del documento de tu Web Server.
Ejecute yiic en la linea de comandos de la siguiente manera:
% YiiRoot/framework/yiic webapp WebRoot/testdrive
Nota: Cuando ejecuta yiic en Mac OS, Linux o Unix, usted deber modificar los
permisos del archivo yiic para poder ejecutarlo. Alternativamente puede correr la
herramienta de la siguiente manera,
% cd WebRoot/testdrive
% php YiiRoot/framework/yiic.php webapp WebRoot/testdrive
Esto crear una aplicacin Yii esqueleto en el directorio WebRoot/testdrive. Esta
aplicacin contiene la estructura de directorios requerida por la mayora de las
aplicaciones Yii.
Sin escribir ni una sola linea de cdigo, nosotros podemos probar nuestra
primera aplicacin Yii ingresando a la siguiente URL en un explorador Web:
http://hostname/testdrive/index.php
Como vemos, la aplicacin contiene tres pginas: homepage (la pgina inicial),
contact (pgina de contacto) y login (pgina de login de usuario). La pgina
inicial muestra informacin de la aplicacin y del estado del usuario logueado, la
pgina de contacto contiene un formulario para rellenar y enviar sus consultas y
la pgina de login de usuario permite a los mismos autenticarse para acceder a
contenidos que necesitan privilegios de acceso. Mire las siguientes pantallas
para ms detalles.
Home page
Contact page
Contact page with input errors
Contact page with success
Login page
The 'User' class has been successfully created in the following file:
D:\wwwroot\testdrive\protected\models\User.php
If you have a 'db' database connection, you can test it now with:
$model=User::model()->find();
print_r($model);
Crud 'user' has been successfully created. You may access it via:
http://hostname/path/to/index.php?r=user
En el cdigo anterior utilizamos el comando yiic shell para interactuar con la
aplicacin esqueleto. Hemos ejecutado dos comandos: model User y crud User.
El primero genera la clase Modelo para la tabla Usermientras que el segundo lee
el modelo User y genera el cdigo necesario para las operaciones CRUD.
Nota: Usted se puede encontrar con errores del estilo "...could not find driver", a
pesar de que el script de verificacin de requerimientos le haya indicado que
tiene habilitado PDO y el driver PD correspondiente. Si esto ocurre puede
intentar correr la herramienta yiic de la siguiente manera:
% php -c path/to/php.ini protected/yiic.php shell
donde path/to/php.ini representa el archivo PHP ini correcto.
Vamos a disfrutar de nuestro trabajo navegando a la siguiente URL:
http://hostname/testdrive/index.php?r=user
Esto nos mostrar una listado de usuarios que se encuentran como entradas de
la tabla User. Como nuestra tabla se encuentra vaca en este momento no ver
ningn dato.
Haga click en el enlace New User de la pgina. Si no estamos logueados con
anterioridad se nos redireccionar a la pgina de login de usuario. Luego de
loguearse usted ver un formulario de entrada que nos permitir agregar un
nuevo usuario a nuestra tabla. Complete el formulario y haga click en el
botnCreate. Si tiene algn tipo de error de ingreso, un bonito error se le
mostrar que previene que grabemos nuestro usuario hasta que no sea correcto.
Volviendo a la lista de usuarios podremos ver el nuevo usuario agregado en la
lista.
Repita el paso anterior para agregar ms usuarios. Fijese que la lista de usuarios
contiene paginacin automtica de los datos de usuario si agrega muchos para
ser mostrados en una sola pgina.
Si nos logueamos como administrador utilizando admin/admin podremos ver la
pgina de administracin en la siguiente URL:
http://hostname/testdrive/index.php?r=user/admin
Esto nos mostrar una tabla de entradas de usuarios. Podemos clickear en las
celdas de los titulos para ordenar los datos de acuerdo a esa columna. Este
cuadro tambin contiene paginacin en caso de que la cantidad de entradas de
usuarios sea mayor a las que se muestran en una pgina.
Todas estas bellas caractersticas han sido creadas sin que tengamos que
escribir ni una sola linea de cdigo!
User admin page
Create new user page
$Id: quickstart.first-app.txt 723 2009-02-21 18:14:05Z sebathi $
14. En la mayora de los casos, el escript de entrada de una aplicacin Yii contiene un
cdigo tn simple como el siguiente,
22. Este script incluye el archivo principal de Yii framework yii.php, crea la instancia de
aplicacin web con la configuracin especificada y inicia su ejecucin.
23. 1. Modo Debug
24. Una aplicacin Yii puede correr en modo debug o modo produccin segn el valor de la
constante YII_DEBUG. Por predeterminado el valor de esta constante es false lo que
significa modo produccin. Para correr su aplicacin en modo debug defina esta
constante con el valor true antes de incluir el archivo yii.php. Ejecutar aplicaciones en
modo debug es menos eficienta ya que debe mantener los logs internamente. Por otro
lado el modo debug es de mucha ayuda durante la etapa de desarrollo ya que provee
informacin de debug rica cuando ocurre el error.
Aplicacin (Application)
Aplicacin (Application) representa la el contexto de ejecucin de cada pedido a
la aplicacin. Su principal tarea es resolver el pedido del usuario y dispararlo al
controlador apropiado para procesamiento futuro. Tambin se utiliza como el
lugar principal para configuraciones que deben estar en el nivel de aplicacin.
Por esta razn application es tambin llamado front-controller (controlador
principal).
Application es creado como un singleton por el script de entrada. El singleton
Application puede ser accedido en cualquier lugar mediante Yii::app().
1. Configuracin de Aplicacin
Por predeterminado, application es una instancia de CWebApplication. Para
customizarlo normalmente se provee un archivo de configuracin (o un arreglo)
para inicializar los valores de sus propiedades cuando la instancia application es
creada. Una alternativa de personalizar la aplicacin es
extender CWebApplication.
La configuracin es un arreglo de pares llave-valor (key-value). Cada par
representa el nombre de una propiedad de la instancia de la aplicacin y cada
valor representa el valor inicial de la correspondiente propiedad. Por ejemplo, la
siguiente configuracin configura las propiedades name y defaultController de
application.
array(
'name'=>'Yii Framework',
'defaultController'=>'site',
)
Usualmente guardamos la configuracin en un archivo de script PHP separado
(ejemplo:protected/config/main.php). Dentro del script retornamos el arreglo de
configuracin como a continuacin:
return array(...);
Para aplicar estas configuraciones pasamos el nombre del archivo de
configuracin como parametro al constructor de application o
a Yii::createWebApplication() como en el siguiente ejemplo el cual es usualmente
utilizado en el Script de entrada:
$app=Yii::createWebApplication($configFile);
Tip: Si la configuracin de la aplicacin es muy compleja, podemos dividirla en
varios archivos en donde cada uno devuelve una parte del arreglo de
configuracin. Para eso, en el archivo de configuracin llamamos a la funcion
PHP include() para incluir el resto de los archivos de configuracin y fusionarlos
en un arreglo de configuracin completo.
2. Directorio Base de Application
El directorio base de Application refiere a la ruta de directorio que contiene todos
los scripts PHP sensibles de seguridad y datos de la misma. Por predeterminado
es un subdirectorio llamado protected que se encuentra bajo el directorio que
contiene el Script de Entrada. Puede ser modificado configurando la
propiedad basePathen la
configuracin de application.
Las cosas que contiene el directorio base deben ser protegidas para que no sean
accesibles por usuarios Web. Con el Apache HTTP server esto se realiza
facilmente creando un archivo .htaccess dentro del directorio base. El contenido
del archivo .htaccess debe ser el siguiente:
deny from all
3. Componentes de Application
Las funcionalidades de la aplicacin pueden ser facilmente customizadas y
enriquecidas con la arquitectura flexible de componentes. Application administra
un juego de componentes de aplicacin en los que cada uno implementa
caractersticas especficas. Por ejemplo, appliction resuleve un pedido de usuario
con la ayuda de los componentes CUrlManager y CHttpRequest.
Configurando la propiedad components de application, podemos personalizar la
class y propiedades de cada uno de los componentes utilizados en application.
Por ejemplo podemos configurara el componenteCMemCache para que utilice
multiples servers memcache para realizar el cacheo,
array(
......
'components'=>array(
......
'cache'=>array(
'class'=>'CMemCache',
'servers'=>array(
array('host'=>'server1', 'port'=>11211, 'weight'=>60),
array('host'=>'server2', 'port'=>11211, 'weight'=>40),
),
),
),
)
En el ejemplo anterior agregamos el elemento cache en el arreglo components.
El elemento cache define que la clase del componente ser CMemCache y la
propiedadservers` debe ser inicializada como lo indica.
Para acceder a un componente de application utilice Yii::app()->ComponentID,
en donde ComponentIDindica el ID del componente que desea
(ejemplo: Yii::app()->cache).
Un componente de aplicacin puede ser deshabilitado mediante su configuracin
indicando la propiedadenabled con un valor false en su configuracin. En el caso
de intentar acceder a un componente deshabilitado, application le devolver Null.
Tip: Por predeterminado, los componentes de application son creados cuando se
necesitan. Esto quiere decir que los componentes no sern creados si estos no
son utilizados durante el request del usuario. Como resultado de esto, la
performance no se vera degradada an si la aplicacin es configuradad con
muchos componentes. Algunos componentes de aplicacin deben ser creados
sin importar si ellos son accedidos o no. Para esto, liste los IDs en la
propiedad preload de la aplicacin.
4. Componentes del nucleo de Application
Yii predefine un juego de compoenentes de aplicacin que proveen
caracteristicas comunes en toda la aplicacin Web. Por ejemplo, el
componente request es usado para resolver pedidos de usuarios y proveer de
informacin como URL, cookies. Configurando las propiedades de estos
componentes podemos cambiar el comportamiento de casi todos los aspectos de
Yii.
Abajo se encuentra la lista de componentes predeclarados por CWebApplication.
assetManager: CAssetManager - administra la publicacin de archivos
privados.
authManager: CAuthManager - Administra el control de acceso basado en
roles (role-based access control - RBAC).
cache: CCache - provee funcionalidad de cacheo de datos. Nota: se debe
especificar la clase actual (ejemplo: CMemCache, CDbCache) o Null ser
retornado cuando se acceda a este componente.
clientScript: CClientScript - Administra los scripts de cliente (javascripts y
CSS).
coreMessages: CPhpMessageSource - provee de los mensajes de nucleo
traducidos utilizados por Yii framework.
db: CDbConnection - provee la conexin a la base de datos. Nota: debe
configurar la propiedadconnectionString para poder utilizar este
componente.
errorHandler: CErrorHandler - maneja los errores y excepciones PHP no
advertidas.
messages: CPhpMessageSource - Provee mensajes traducidos utilizados
por la aplicacin Yii.
request: CHttpRequest - Provee informacin relacionada con el request.
securityManager: CSecurityManager - provee servicios relacionados con
seguridad como son hashing y encriptacin.
session: CHttpSession - provee funcionalidades relacionadas con la sesin.
statePersister: CStatePersister - provee mtodos globles de persistencia
de estado.
urlManager: CUrlManager - provee funcionalidad para parseo de URL y
creacin.
user: CWebUser - representa la informacin de identidad del usuario
actual.
themeManager: CThemeManager - maneja temas (themes).
5. Ciclos de vida de la Aplicacin
Cuando se maneja un un pedido de usuario, la aplicacin realizar el siguiente
ciclo de vida:
1. Configurar el autocargado de clases y el manejador de errores;
2. Registrar los componentes del nucleo de la aplicacin;
3. Cargar la configuracin de la aplicacin;
4. Inicializar la aplicacin mediante CApplication::init()
Carga de compoenentes de aplicacin static;
5. Ejecuta el evento onBeginRequest;
6. Procesa el pedido de usuario:;
Resuelve el pedido de usuario;
Crea el controlador
Ejecuta el controlador;
7.Ejecuta el evento onEndRequest;
Controlador (Controller)
Un controlador es una instancia de CController o una de las clases que lo
heredan. Es creado por la aplicacin cuando un usuario realiza un pedido para
ese controlador. Cuando un controlador se ejecuta se realizar el pedido de la
accin que utiliza los modelos necesarios y muestra la informacin a travez de la
vista apropiada. Una accin, en su forma ms simple, es un m;etodo de la clase
controlador cuyo nombre comienza con action.
Un controlador tiene un a accin predeterminada. Cuando el usuario no
especifica que accin se debe ejecutar, esta ser la que se ejecute. Por
predeterminado la accin default tiene el nombre de index. Puede ser
personalizada modificando la configuracin CController::defaultAction.
Abajo se encuentra el minimo cdigo de una clase controlador. Dado que este
controlador no tiene ninguna accin definida, pedirle resultar en una excepcin.
class SiteController extends CController
{
}
1. Ruta (Route)
Los controladores y acciones estn definidas por IDs. El ID del controlador se
encuentra en la forma depath/to/xyz el cual es interpretado como el archivo de
clase controladorprotected/controllers/path/to/XyzController.php,
donde xyz debe ser remplazada por el nombre de su controlador
(ejemplo: post corresponde a protected/controllers/PostController.php). El ID de
accin es el nombre del metodo sin el prefijo action. Por ejemplo si el controlador
contiene el mtodoactionEdit el ID de la accin correspondiente ser edit.
Nota: Antes de la versin 1.0.3, el formato del id del controlador
era path.to.xyz en vez depath/to/xyz.
Los usuarios realizan pedidos por un controlador y accin en trminos de ruta.
Una ruta se encuentra formada por la concatenacin de un ID de controlador y
un ID de accin separados por una barra. Por ejemplo la rutapost/edit se refiere
a PostController y a su accin edit. Por predeterminado la
urlhttp://hostname/index.php?r=post/edit` realiza el pedido a el ese contlador y
esa accin.
Nota: Por predeterminado las rutas distinguen maysculas de minsculas.
Desde la versin 1.0.1 es posible utilizar rutas que no distingan maysculas de
minsculas modificando en la configuracin de la aplicacin la
propiedad CUrlManager::caseSensitive en false. Cuando esta propiedad no est
activada, asegurese de seguir las convencion de que los directorios que
contienen controladores deben ser llamados con minsculas y que
ambos, controller map y action map usan claves en minsculas.
Desde la versin 1.0.3 una aplicacin puede contener modules. La ruta de una
accin de controlador dentro de un mdulo cumple es de la
forma moduleID/controllerID/actionID. Para ms informacin y detalle vea
la seccin acerca de mdulos.
2. Instanciacin de Controlador
Una instancia de controlador es creada cuando CWebApplication maneja un
pedido de usuario. Dado el ID del controlador, la aplicacin utilizar las
siguientes reglas para determinar cual es la clase del controlador y cual la ruta al
archivo de clase.
Si CWebApplication::catchAllRequest se encuentra especificado, el
controlador ser creado basado en esta propiedad y se ignorar el ID de
controlador especificado por el usuario. Esto es usado mayoritariamente
para dejar la aplicacin en un modo de mantenimiento y muestre una
pgina con informacin esttica.
Si el ID se encuentra en CWebApplication::controllerMap, la configuracin
de controlador correspondiente se utilizar para crear la instancia del
controlador.
Si el ID se encuentra en el formato 'path/to/xyz', la clase de controlador
assumida ser XyzCOntroller y el archivo de clase correspondiente
ser protected/controllers/path/to/XyzController.php. Por ejemplo si el ID
del controlador es admin/user ser resuelto por el
controlador UserController y el archivo de
clase protected/controllers/admin/UserController.php. En caso de que el
archivo de clase no exista, un error 404 CHttpException ser lanzado.
En el caso que se utilizen modules (disponibles desde la versin 1.0.3), El
proceso descripto anteriormente es ligeramente diferente. En particular, la
aplicacin verificar si el ID refiere a un controlador dentro de un mdulo y si
esto es as, el mdulo ser instanciado y luego se instanciar el controlador.
3. Accion (Action)
Como lo mencionamos anteriormente una accin puede ser definida mediante su
nombre y comenzando con la palabra action. Una forma ms avanzada de
realizar esto es definir una clase accin y pedirle al controlador que la instancie
cuando es requerida. Esto permite que las acciones sean reusadas y genera ms
reusabilidad.
Para definir una nueva clase accin, realice lo siguiente:
class UpdateAction extends CAction
{
public function run()
{
// place the action logic here
}
}
Para que el controlador sepa que debe utilizar esta accin hacemos override del
mtodo actions() en nuestra clase controlador de la siguiente manera:
class PostController extends CController
{
public function actions()
{
return array(
'edit'=>'application.controllers.post.UpdateAction',
);
}
}
En el ejemplo anterior usamos la ruta
alias application.controllers.post.UpdateAction para especificar que el archivo
clase de la accin es protected/controllers/post/UpdateAction.php.
Escribiendo acciones basados en clases podemos organizar la applicacin de
manera modular. Por ejemplo, la siguiente estructura de directorios puede ser
utilizada para organizar el cdigo de los controladores:
protected/
controllers/
PostController.php
UserController.php
post/
CreateAction.php
ReadAction.php
UpdateAction.php
user/
CreateAction.php
ListAction.php
ProfileAction.php
UpdateAction.php
4. Filtros
Los filtros son una pieza de codigo que se configura para ser ejecutada antes y/o
despus de que una accin del controlador sea ejecutada. Por ejemplo, un filtro
de control de acceso puede ser ejecutado para asegurarse de que el usuario ha
sido autenticado con anterioridad antes de ejecutar cierta accin; un filtro de
performance puede ser utilizado para medir el tiempo que tarda una accin en
ejecutarse.
Una accin puede tener mltiples filtros. Los filtros son ejecutados en el orden en
el que aparecen en la lista de filtros. Un filtro puede prevenir la ejecucin de la
accin y el resto de los filtros de la lista que no han sido ejecutados.
Un filtro puede ser definido como un mtodo en la clase controlador. El nombre
del mtodo debe iniciar confilter. Por ejemplo, la existencia de un
mtodo filterAccessControl define un filtro llamado llamado `accessControl. El
mtodo de filtro debe ser definido de la siguiente manera:
public function filterAccessControl($filterChain)
{
// call $filterChain->run() to continue filtering and action execution
}
en donde $filterChain es una instancia de CFilterChain que representa la lista de
filtro asociada con la accion pedida. Dentro del mtodo del filtro podemos llamar
a $filterChain->run() para continuar filtrando la ejecucin de la accin.
A su vez, un filtro tambin puede ser un una instancia de CFilter o una clase que
la herede. El siguiente cdigo define una nueva clase filtro:
class PerformanceFilter extends CFilter
{
protected function preFilter($filterChain)
{
// logic being applied before the action is executed
return true; // false if the action should not be executed
}
private $_identity;
// in register scenario
$model=new User('register');
if(isset($_POST['User']))
$model->attributes=$_POST['User'];
So why do we use such a policy to determine if an attribute is safe or not? The
rationale behind is that if an attribute already has one or several validation rules
to check its validity, what else should we worry about it?
It is important to remember that validation rules are used to check user input
data rather than the data that we generate in the code (e.g. timestamp, auto-
generated primary key). Therefore, DO NOT add validation rules for those
attributes which do not expect inputs from end-users.
Sometimes, we want to declare an attribute to be safe, even though we do not
really have any specific rule for it. An example is an article's content attribute
which can take any user input. We can use the special safe rule to achieve this
goal:
array('content', 'safe')
For completeness, there is also an unsafe rule which is used to explicitly declare
an attribute to be unsafe:
array('permission', 'unsafe')
The unsafe rule is rarely used, and it is an exception to our previous definition of
safe attributes.
For data entries that are not safe, we need to assign them to the corresponding
attributes using individual assign statements, like the following:
$model->permission='admin';
$model->id=1;
4. Triggering Validation
Once a model is populated with user-submitted data, we can
call CModel::validate() to trigger the data validation process. The method returns
a value indicating whether the validation is successful or not.
ForCActiveRecord models, validation may also be automatically triggered when
we call its CActiveRecord::save()method.
We can set a scenario with the scenario property and therewith indicate which
set of validation rules should be applied.
Validation is performed in a scenario basis. The scenario property specifies which
scenario the model is being used in and which set of validation rules should be
used. For example, in the login scenario, we only want to validate
the username and password inputs of a user model; while in
the register scenario, we need to validate more inputs, such as email, address,
etc. The following example shows how to perform validation in
the register scenario:
// creates a User model in register scenario. It is equivalent to:
// $model=new User;
// $model->scenario='register';
$model=new User('register');
<div class="row">
<?php echo CHtml::activeLabel($model,'username'); ?>
<?php echo CHtml::activeTextField($model,'username') ?>
</div>
<div class="row">
<?php echo CHtml::activeLabel($model,'password'); ?>
<?php echo CHtml::activePasswordField($model,'password') ?>
</div>
<div class="row">
<?php echo $form->label($model,'username'); ?>
<?php echo $form->textField($model,'username') ?>
</div>
<div class="row">
<?php echo $form->label($model,'password'); ?>
<?php echo $form->passwordField($model,'password') ?>
</div>
<div class="row rememberMe">
<?php echo $form->checkBox($model,'rememberMe'); ?>
<?php echo $form->label($model,'rememberMe'); ?>
</div>
$post=new Post;
echo $post->title; // esto mostrar: por favor ingrese un ttulo
Desde la versin 1.0.2, a un atributo se le puede asignar un valor de
tipo CDbExpression antes de que el registro sea guardado (tante en la insercin
como en la actualizacin) en la base de datos. Por ejemplo, para guardar el
timestamp devuelto por la funcion NOW() de MySQL, podemos usar el siguiente
cdigo:
$post=new Post;
$post->createTime=new CDbExpression('NOW()');
// $post->createTime='NOW()'; no funcionar porque
// 'NOW()' ser tratado como una string
$post->save();
4. Leyendo Registros
Para leer datos en una base de datos, podemos llamar a uno de los
mtodos find como sigue.
// encontrar el primer registro que cumpla la condicin especificada
$post=Post::model()->find($condition,$params);
// encontrar la fila con la clave primaria especificada
$post=Post::model()->findByPk($postID,$condition,$params);
// encontrar la fila con los valores de los atributos especificados
$post=Post::model()->findByAttributes($attributes,$condition,$params);
// encontrar la primer fila usando la sentencia SQL especificada
$post=Post::model()->findBySql($sql,$params);
En lo anterior, llamamos al mtodo find con Post::model(). Recordemos que el
mtodo esttico model()es requerido por toda clase AR. El mtodo devuelve una
instancia que es usada para acceder a los mtodos de nivel de clase (algo similar
a los mtodos de clase estticos) en un contexto de objetos.
Si el mtodo find encuentra una fila que cumpla con las condiciones de la
consulta, devolver una instancia de Post cuyas propiedades contendran los
correspondientes valores de las columnas en la fila de la tabla. Podemos
entonces leer los valores cargados como lo hacemos con las propiedades de
objetos normales, por ejemplo, echo $post->title;
El mtodo find devolver null si nada puede ser encontrado en la base de datos
con las condiciones de la consulta dada.
Cuando llamammos a find, usamos $condition y $params para especificar las
condiciones de la consulta. Aqu, $condition puede ser una string representando
la clusula WHERE en una sentencia SQL, y ``$paramses un arreglo de
parmetros cuyos valores deben ser enlazados a los marcadores de posicin
en$condition`. Por ejemplo,
// find the row with postID=10
$post=Post::model()->find('postID=:postID', array(':postID'=>10));
Podemos tambien usar $condition para especificar condiciones de consultas ms
complejas. En vez de una strign, dejamos a $condition ser una instancia
de CDbCriteria, que nos permite especificar otras condiciones ademas de la
clusula WHERE. Por ejemplo,
$criteria=new CDbCriteria;
$criteria->select='title'; // seleccionar solo la columna 'title'
$criteria->condition='postID=:postID';
$criteria->params=array(':postID'=>10);
$post=Post::model()->find($criteria); // $params no es necesario
Notar que, cuando usamos CDbCriteria como condicin de la consulta, el
parmetro $params ya no es necesario, puesto que puede ser especificado
en CDbCriteria, como se muestra arriba.
Una forma alternativa a CDbCriteria es pasar un arreglo al mtodo find. Las
claves y los valores del arreglo corresponden a las propiedades del criterio y sus
valores respectivamente. El ejemplo anterior puede ser reescrito como sigue,
$post=Post::model()->find(array(
'select'=>'title',
'condition'=>'postID=:postID',
'params'=>array(':postID'=>10),
));
Informacin: Cuando una condicin de consulta es sobre que algunas columnas
tengan valores especficos, podemos usar findByAttributes(). Dejaremos al
parmetro $attributes ser un arreglo de los valores indexados por los nombres
de las columnas. En algunos frameworks, esta tarea puede ser lograda llamando
mtodos como findByNameAndTitle. Aunque este enfoque parece atractivo,
frecuentemente causa confusin, conflictos y cuestiones como sensibilidad a
maysculas/minsculas de los nombres de columna.
Cuando mltiples filas de datos coinciden con una condidicin de consulta
especificada, podemos traerlas todas juntas usando los siguientes
mtodos findAll, cada uno de los cuales tiene su mtodo contrapartefind, que ya
mencionamos anteriormente.
// encontrar todas las filas que cumplan la condicin especificada
$posts=Post::model()->findAll($condition,$params);
// encontrar todas las filas con la clave primaria especificada
$posts=Post::model()->findAllByPk($postIDs,$condition,$params);
// encontrar todas las filas con los valores de atributos especificados
$posts=Post::model()->findAllByAttributes($attributes,$condition,$params);
// encontrar todas las filas usando la sentencia SQL especificada
$posts=Post::model()->findAllBySql($sql,$params);
Si nada coincide con la condicin de la consulta, findAll devolver un arreglo
vaco. Esto es diferente afind, quien devuelve null si no se encuentra cosa
alguna.
Adems de los mtodos find y findAll descriptos anteriormente, por conveniencia
tambin se proveen los siguientes mtodos:
// obtener el nmero de filas que cumplan la condicin especificada
$n=Post::model()->count($condition,$params);
// obtener el nmero de filas usando la sentencia SQL especificada
$n=Post::model()->countBySql($sql,$params);
// comprobar si existe al menos una fila que cumpla la condicin especificada
$exists=Post::model()->exists($condition,$params);
5. Actualizando Registros
Luego de que una isntancia AR sea rellenada con valores, podemos cambiarlos y
volver a guardarlos en la tabla de la base de datos.
$post=Post::model()->findByPk(10);
$post->title='nuevo titulo del post';
$post->save(); // guardar cambios en la base de datos
Como podemos ver, usamos el mismo mtodo save() para ejecutar las
operaciones de insercin y actualizacin. Si una instancia AR es creada usando el
operador new, llamar a save() insertar una nueva fila en la tabla de la base de
datos; si la instancia AR es el resultado de la llamada a algn
mtodo find ofindAll, llamar a save() actualizar la fila existente en la tabla. De
hecho, podemos usarCActiveRecord::isNewRecord para decir si una instancia AR
es nueva o no.
Tambin es posible actualizar una o varias filas en una tabla de la base de datos
sin cargarlas primero. AR provee los siguientes convenientes mtodos de nivel
de clase para este propsito:
// actualizar las filas que coincidan con la condicin especificada
Post::model()->updateAll($attributes,$condition,$params);
// actualizar las filas que coincidan con la condicin especificada y con la(s)
clave(s) primaria(s)
Post::model()->updateByPk($pk,$attributes,$condition,$params);
// update counter columns in the rows satisfying the specified conditions
Post::model()->updateCounters($counters,$condition,$params);
En lo anterior, $attributes es un arreglo de valores de columna indexado por
nombres de columna;$counters es un arreglo de valores incrementales
indexados por nombres de columna; y $condition y$params son como se
describi en las subsecciones previas.
6. Borrando Registros
Podemos tambin borrar una fila de datos si una instancia AR ha sido rellenada
con esa fila.
$post=Post::model()->findByPk(10); // asumiendo que existe un post cuyo ID es
10
$post->delete(); // borra la fila de la tabla de la base de datos
Nota, luego del borrado, la instancia AR permanece intacta, pero la
correspondiente fila en la tabla de la base de datos ya no est.
Los siguientes mtodos de nivel de clase se proveen para borrar filas sin la
necesidad de cargarlas primero:
// borra todas las filas que coincidan con la condicin especificada
Post::model()->deleteAll($condition,$params);
// borra todas las filas que coincidan con la condicin especificada y con la(s)
clave(s) primaria(s)
Post::model()->deleteByPk($pk,$condition,$params);
7. Validacin de Datos
Cuando insertamos o actualizamos una fila, frecuentemente necesitamos
comprobar que los valores de las columnas cumplen ciertas reglas. Esto es
especialmente importante si los valores de la columna son provistos por usuarios
finales. En general, nunca debemos confiar en nada que provenga del lado del
cliente.
AR ejecuta la validacin de datos automticamente cuando se invoca a save().
La validacin est basada en las reglas especificadas en el mtodo rules() de la
clase AR. Para ms detalles acerca de como especificar reglas de validacin, ver
la seccin Declarando Relgas de Validacin. A continuacin est el flujo de
trabajo tpico necesario para guardar un registro:
if($post->save())
{
// los datos son vlidos y estn insertados/actualizados exitosamente
}
else
{
// los datos no son vlidos. Llamar a getErrors() para obtener los mensajes de
error
}
Cuando los datos a insertar o actualizar son enviados por usarios finales en un
formulario HTML, necesitamos asignarlos a las correspondientes propiedades AR.
Podemos hacerlo como sigue:
$post->title=$_POST['title'];
$post->content=$_POST['content'];
$post->save();
Si existen muchas columnas, veremos una larga lista de dichas asignaciones.
Esto se puede aliviar haciendo uso de la propiedad attributes como se muestra a
continuacin. Ms detalles pueden ser encontrados en la seccin Asegurando las
Asignaciones de Atributos y en la seccin Creating Action.
// asumimos que $_POST['Post'] es un arreglo de valores de columna indexados
por nombres de columna
$post->attributes=$_POST['Post'];
$post->save();
8. Comparando Registros
Como las filas de las tablas, las instancias AR estn identificadas de manera
nica por los valores de su clave primaria. Por lo tanto, para comparar dos
instancias AR, solo es necesario comparar los valores de sus claves primarias,
asumiendo que pertenezcan a la misma clase AR. Sin embargo, una manera ms
simple es llamar aCActiveRecord::equals().
Info: Informacin: A diferencia de la implementacin de AR en otros frameworks,
Yii soporta claves primaris compuestas en su AR. Una clave primaria consiste de
dos o ms columnas. Correspondientemente, en Yii el valor de la clave primaria
est representado como un arreglo. La propiedad primaryKey nos da el valor de
la clave primaria de una instancia AR.
9. Personalizacin
CActiveRecord provee algunos mtodos que pueden ser sobreescritos en las
clases que la heredan para personalizar su flujo de trabajo.
beforeValidate y afterValidate: estos mtodos son invocados antes y
despus de que la validacin sea ejecutada.
beforeSave y afterSave: estos mtodos son invocados antes y despus de
que la instancia AR sea guardada.
beforeDelete y afterDelete: estos mtodos son invocados antes y despus
de que la instancia AR sea borrada.
afterConstruct: este mtodo es invocado por cada instancia AR creada
usando el operador new.
afterFind: este mtodo es invocado por cada instancia AR creada como
resultado de una bsqueda.
10. Usando Transacciones con AR
Cada instancia AR contiene una propiedad llamada dbConnection que es una
instancia de CDbConnection. Por lo tanto podemos utilizar la
caracterstica transaction provista por el DAO de Yii si se desea cuando
trabajamos con AR:
$model=Post::model();
$transaction=$model->dbConnection->beginTransaction();
try
{
// encontar y guardad son dos pasos que pueden ser intervenidos por otra
solicitud
// por lo tanto usaremos una transaccin para asegurar su consistencia e
integridad
$post=$model->findByPk(10);
$post->title='nuevo ttulo del post';
$post->save();
$transaction->commit();
}
catch(Exception $e)
{
$transaction->rollBack();
}
Registro Activo Relacional
Ya hemos visto como usar Registro Activo (AR) para seleccionar datos desde una
tabla sencilla de la base de datos. En esta seccin, describiremos como usar AR
para unir varias tablas relacionadas de la base de datos y obtener de vuelta el
conjunto de datos unidos.
Para usar AR relacional, se requiere que las relaciones claver primaria-fornea
estn bien definidas entre las tablas que necesitan ser unidas. AR depende de
los metadatos acerca de estas relaciones para determinar como unir las tablas.
Nota: Comenzando desde la versin 1.0.1, podemos usar AR relacional an si no
definimos ninguna clave fornea en nuestra base de datos.
Por sencillez, usamos el esquema de la base de datos mostrado en el siguiente
diagrama entidad-relacion (ER) para ilustrar ejemplos en esta seccin.
Diagrama ER
$auth->createOperation('createPost','create a post');
$auth->createOperation('readPost','read a post');
$auth->createOperation('updatePost','update a post');
$auth->createOperation('deletePost','delete a post');
$bizRule='return Yii::app()->user->id==$params["post"]->authID;';
$task=$auth->createTask('updateOwnPost','update a post by author himself',
$bizRule);
$task->addChild('updatePost');
$role=$auth->createRole('reader');
$role->addChild('readPost');
$role=$auth->createRole('author');
$role->addChild('reader');
$role->addChild('createPost');
$role->addChild('updateOwnPost');
$role=$auth->createRole('editor');
$role->addChild('reader');
$role->addChild('updatePost');
$role=$auth->createRole('admin');
$role->addChild('editor');
$role->addChild('author');
$role->addChild('deletePost');
$auth->assign('reader','readerA');
$auth->assign('author','authorB');
$auth->assign('editor','editorC');
$auth->assign('admin','adminD');
Once we have established this hierarchy, the authManager component
(e.g. CPhpAuthManager,CDbAuthManager) will load the authorization items
automatically. Therefore, we only need to run the above code one time, and NOT
for every request.
Info: While the above example looks long and tedious, it is mainly for
demonstrative purpose. Developers will usually need to develop some
administrative user interfaces so that end users can use to establish an
authorization hierarchy more intuitively.
9. Using Business Rules
When we are defining the authorization hierarchy, we can associate a role, a task
or an operation with a so-called business rule. We may also associate a business
rule when we assign a role to a user. A business rule is a piece of PHP code that
is executed when we perform access checking. The returning value of the code is
used to determine if the role or assignment applies to the current user. In the
example above, we associated a business rule with the updateOwnPost task. In
the business rule we simply check if the current user ID is the same as the
specified post's author ID. The post information in the $params array is supplied
by developers when performing access checking.
Access Checking
To perform access checking, we first need to know the name of the authorization
item. For example, to check if the current user can create a post, we would
check if he has the permission represented by the createPostoperation. We then
call CWebUser::checkAccess to perform the access checking:
if(Yii::app()->user->checkAccess('createPost'))
{
// create post
}
If the authorization rule is associated with a business rule which requires
additional parameters, we can pass them as well. For example, to check if a user
can update a post, we would pass in the post data in the$params:
$params=array('post'=>$post);
if(Yii::app()->user->checkAccess('updateOwnPost',$params))
{
// update post
}
Using Default Roles
Many Web applications need some very special roles that would be assigned to
every or most of the system users. For example, we may want to assign some
privileges to all authenticated users. It poses a lot of maintenance trouble if we
explicitly specify and store these role assignments. We can exploit default
roles to solve this problem.
A default role is a role that is implicitly assigned to every user, including both
authenticated and guest. We do not need to explicitly assign it to a user.
When CWebUser::checkAccess is invoked, default roles will be checked first as if
they are assigned to the user.
Default roles must be declared in the CAuthManager::defaultRoles property. For
example, the following configuration declares two roles to be default
roles: authenticated and guest.
return array(
'components'=>array(
'authManager'=>array(
'class'=>'CDbAuthManager',
'defaultRoles'=>array('authenticated', 'guest'),
),
),
);
Because a default role is assigned to every user, it usually needs to be
associated with a business rule that determines whether the role really applies to
the user. For example, the following code defines two
roles,authenticated and guest, which effectively apply to authenticated users
and guest users, respectively.
$bizRule='return !Yii::app()->user->isGuest;';
$auth->createRole('authenticated', 'authenticated user', $bizRule);
$bizRule='return Yii::app()->user->isGuest;';
$auth->createRole('guest', 'guest user', $bizRule);
Theming
Theming is a systematic way of customizing the outlook of pages in a Web
application. By applying a new theme, the overall appearance of a Web
application can be changed instantly and dramatically.
In Yii, each theme is represented as a directory consisting of view files, layout
files, and relevant resource files such as images, CSS files, JavaScript files, etc.
The name of a theme is its directory name. All themes reside under the same
directoryWebRoot/themes. At any time, only one theme can be active.
Tip: The default theme root directory WebRoot/themes can be configured to be
a different one. Simply configure the basePath and the baseUrl properties of
the themeManager application component to be the desired ones.
1. Using a Theme
To activate a theme, set the theme property of the Web application to be the
name of the desired theme. This can be done either in the application
configuration or during runtime in controller actions.
Note: Theme name is case-sensitive. If you attempt to activate a theme that
does not exist,Yii::app()->theme will return null.
2. Creating a Theme
Contents under a theme directory should be organized in the same way as those
under the application base path. For example, all view files must be located
under views, layout view files under views/layouts, and system view files
under views/system. For example, if we want to replace the create view
ofPostController with a view in the classic theme, we should save the new view
file asWebRoot/themes/classic/views/post/create.php.
For views belonging to controllers in a module, the corresponding themed view
files should also be placed under the views directory. For example, if the
aforementioned PostController is in a module named forum, we should save
the create view file as WebRoot/themes/classic/views/forum/post/create.php. If
theforum module is nested in another module named support, then the view file
should beWebRoot/themes/classic/views/support/forum/post/create.php.
Note: Because the views directory may contain security-sensitive data, it should
be configured to prevent from being accessed by Web users.
When we call render or renderPartial to display a view, the corresponding view
file as well as the layout file will be looked for in the currently active theme. And
if found, those files will be rendered. Otherwise, it falls back to the default
location as specified by viewPath and layoutPath.
Tip: Inside a theme view, we often need to link other theme resource files. For
example, we may want to show an image file under the
theme's images directory. Using the baseUrl property of the currently active
theme, we can generate the URL for the image as follows,
Yii::app()->theme->baseUrl . '/images/FileName.gif'
Below is an example of directory organization for an application with two
themes basic and fancy.
WebRoot/
assets
protected/
.htaccess
components/
controllers/
models/
views/
layouts/
main.php
site/
index.php
themes/
basic/
views/
.htaccess
layouts/
main.php
site/
index.php
fancy/
views/
.htaccess
layouts/
main.php
site/
index.php
In the application configuration, if we configure
return array(
'theme'=>'basic',
......
);
then the basic theme will be in effect, which means the application's layout will
use the one under the directorythemes/basic/views/layouts, and the site's index
view will use the one underthemes/basic/views/site. In case a view file is not
found in the theme, it will fall back to the one under
theprotected/views directory.
3. Theming Widgets
Starting from version 1.1.5, views used by a widget can also be themed. In
particular, when we callCWidget::render() to render a widget view, Yii will
attempt to search under the theme folder as well as the widget view folder for
the desired view file.
To theme the view xyz for a widget whose class name is Foo, we should first
create a folder named Foo (same as the widget class name) under the currently
active theme's view folder. If the widget class is namespaced (available in PHP
5.3.0 or above), such as \app\widgets\Foo, we should create a folder
namedapp_widgets_Foo. That is, we replace the namespace separators with the
underscore characters.
We then create a view file named xyz.php under the newly created folder. To this
end, we should have a filethemes/basic/views/Foo/xyz.php, which will be used by
the widget to replace its original view, if the currently active theme is basic.
4. Customizing Widgets Globally
Note: this feature has been available since version 1.1.3.
When using a widget provided by third party or Yii, we often need to customize it
for specific needs. For example, we may want to change the value
of CLinkPager::maxButtonCount from 10 (default) to 5. We can accomplish this
by passing the initial property values when calling CBaseController::widget to
create a widget. However, it becomes troublesome to do so if we have to repeat
the same customization in every place we useCLinkPager.
$this->widget('CLinkPager', array(
'pages'=>$pagination,
'maxButtonCount'=>5,
'cssFile'=>false,
));
Using the global widget customization feature, we only need to specify these
initial values in a single place, i.e., the application configuration. This makes the
customization of widgets more manageable.
To use the global widget customization feature, we need to configure
the widgetFactory as follows:
return array(
'components'=>array(
'widgetFactory'=>array(
'widgets'=>array(
'CLinkPager'=>array(
'maxButtonCount'=>5,
'cssFile'=>false,
),
'CJuiDatePicker'=>array(
'language'=>'ru',
),
),
),
),
);
In the above, we specify the global widget customization for
both CLinkPager and CJuiDatePicker widgets by configuring
the CWidgetFactory::widgets property. Note that the global customization for
each widget is represented as a key-value pair in the array, where the key refers
to the wiget class name while the value specifies the initial property value array.
Now, whenever we create a CLinkPager widget in a view, the above property
values will be assigned to the widget, and we only need to write the following
code in the view to create the widget:
$this->widget('CLinkPager', array(
'pages'=>$pagination,
));
We can still override the initial property values when necessary. For example, if
in some view we want to setmaxButtonCount to be 2, we can do the following:
$this->widget('CLinkPager', array(
'pages'=>$pagination,
'maxButtonCount'=>2,
));
5. Skin
While using a theme we can quickly change the outlook of views, we can use
skins to systematically customize the outlook of the widgets used in the views.
A skin is an array of name-value pairs that can be used to initialize the properties
of a widget. A skin belongs to a widget class, and a widget class can have
multiple skins identified by their names. For example, we can have a skin for
the CLinkPager widget and the skin is named as classic.
In order to use the skin feature, we first need to modify the application
configuration by configuring theCWidgetFactory::enableSkin property to be true
for the widgetFactory application component:
return array(
'components'=>array(
'widgetFactory'=>array(
'enableSkin'=>true,
),
),
);
Please note that in versions prior to 1.1.3, you need to use the following
configuration to enable widget skinning:
return array(
'components'=>array(
'widgetFactory'=>array(
'class'=>'CWidgetFactory',
),
),
);
We then create the needed skins. Skins belonging to the same widget class are
stored in a single PHP script file whose name is the widget class name. All these
skin files are stored under protected/views/skins, by default. If you want to
change this to be a different directory, you may configure the skinPath property
of thewidgetFactory component. As an example, we may create
under protected/views/skins a file namedCLinkPager.php whose content is as
follows,
<?php
return array(
'default'=>array(
'nextPageLabel'=>'>>',
'prevPageLabel'=>'<<',
),
'classic'=>array(
'header'=>'',
'maxButtonCount'=>5,
),
);
In the above, we create two skins for the CLinkPager widget: default and classic.
The former is the skin that will be applied to any CLinkPager widget that we do
not explicitly specify its skin property, while the latter is the skin to be applied to
a CLinkPager widget whose skin property is specified as classic. For example, in
the following view code, the first pager will use the default skin while the second
the classic skin:
<?php $this->widget('CLinkPager'); ?>
/**
* @param string the symbol of the stock
* @return float the stock price
* @soap
*/
public function getPrice($symbol)
{
//...return stock price for $symbol
}
}
That is all we need to create a Web service! If we try to access the action by
URLhttp://hostname/path/to/index.php?r=stock/quote, we will see a lot of XML
content which is actually the WSDL for the Web service we defined.
Tip: By default, CWebServiceAction assumes the current controller is the service
provider. That is why we define the getPrice method inside
the StockController class.
3. Consuming Web Service
To complete the example, let's create a client to consume the Web service we
just created. The example client is written in PHP, but it could be in other
languages, such as Java, C#, Flex, etc.
$client=new SoapClient('http://hostname/path/to/index.php?r=stock/quote');
echo $client->getPrice('GOOGLE');
Run the above script in either Web or console mode, and we shall see 350 which
is the price for GOOGLE.
4. Data Types
When declaring class methods and properties to be remotely accessible, we
need to specify the data types of the input and output parameters. The following
primitive data types can be used:
str/string: maps to xsd:string;
int/integer: maps to xsd:int;
float/double: maps to xsd:float;
bool/boolean: maps to xsd:boolean;
date: maps to xsd:date;
time: maps to xsd:time;
datetime: maps to xsd:dateTime;
array: maps to xsd:string;
object: maps to xsd:struct;
mixed: maps to xsd:anyType.
If a type is not any of the above primitive types, it is considered as a composite
type consisting of properties. A composite type is represented in terms of a
class, and its properties are the class' public member variables marked
with @soap in their doc comments.
We can also use array type by appending [] to the end of a primitive or
composite type. This would specify an array of the specified type.
Below is an example defining the getPosts Web API which returns an array
of Post objects.
class PostController extends CController
{
/**
* @return Post[] a list of posts
* @soap
*/
public function getPosts()
{
return Post::model()->findAll();
}
}
return array(
'components'=>array(
......,
'viewRenderer'=>array(
'class'=>'CPradoViewRenderer',
),
),
);
By default, CPradoViewRenderer will compile source view files and save the resulting PHP files
under theruntime directory. Only when the source view files are changed, will the PHP files be
re-generated. Therefore, using CPradoViewRenderer incurs very little performance degradation.
Tip: While CPradoViewRenderer mainly introduces some new template tags to make writing
views easier and faster, you can still write PHP code as usual in the source views.
In the following, we introduce the template tags that are supported by CPradoViewRenderer.
Short PHP Tags
Short PHP tags are shortcuts to writing PHP expressions and statements in a view. The
expression tag <%= expression %> is translated into <?php echo expression ?>; while
the statement tag <% statement %>to <?php statement ?>. For example,
is translated into
Component Tags
Component tags are used to insert a widget in a view. It uses the following syntax:
where WidgetClass specifies the widget class name or class path alias, and property initial
values can be either quoted strings or PHP expressions enclosed within a pair of curly brackets.
For example,
would be translated as
Note: The value for showRefreshButton is specified as {false} instead of "false" because
the latter means a string instead of a boolean.
Cache Tags
Cache tags are shortcuts to using fragment caching. Its syntax is as follows,
where fragmentID should be an identifier that uniquely identifies the content being cached, and
the property-value pairs are used to configure the fragment cache. For example,
<cache:profile duration={3600}>
// user profile information here
</cache:profile >
would be translated as
Clip Tags
Like cache tags, clip tags are shortcuts to
calling CBaseController::beginClip and CBaseController::endClip in a view. The syntax is as
follows,
<clip:clipID>
// content for this clip
</clip:clipID >
where clipID is an identifier that uniquely identifies the clip content. The clip tags will be
translated as
<!---
view comments that will be stripped off
--->
1. Overview
Yii represents each console task in terms of a command. A console command is written as a
class extending from CConsoleCommand.
When we use the yiic webapp tool to create an initial skeleton Yii application, we may find two
files under theprotected directory:
yiic: this is an executable script used on Linux/Unix;
yiic.bat: this is an executable batch file used on Windows.
In a console window, we can enter the following commands:
cd protected
yiic help
This will display a list of available console commands. By default, the available commands
include those provided by Yii frameweork (called system commands) and those developed by
users for individual applications (called user commands).
To see how to use a command, we can execute
2. Creating Commands
Console commands are stored as class files under the directory specified
byCConsoleApplication::commandPath. By default, this refers to the
directory protected/commands.
A console command class must extend from CConsoleCommand. The class name must be of
formatXyzCommand, where Xyz refers to the command name with the first letter in upper
case. For example, asitemap command must use the class name SitemapCommand.
Console command names are case-sensitive.
Tip: By configuring CConsoleApplication::commandMap, one can also have command classes in
different naming conventions and located in different directories.
To create a new command, one often needs to override CConsoleCommand::run() or develop
one or several command actions (to be explained in the next section).
When executing a console command, the CConsoleCommand::run() method will be invoked by
the console application. Any console command parameters will be passed to the method as well,
according to the following signature of the method:
where $args refers to the extra parameters given in the command line.
Within a console command, we can use Yii::app() to access the console application instance,
through which we can also access resources such as database connections (e.g. Yii::app()-
>db). As we can tell, the usage is very similar to what we can do in a Web application.
Info: Starting from version 1.1.1, we can also create global commands that are shared by all Yii
applications on the same machine. To do so, define an environment variable
namedYII_CONSOLE_COMMANDS which should point to an existing directory. We can then
put our global command class files under this directory.
3. Console Command Action
Note: The feature of console command action has been available since version 1.1.5.
A console command often needs to handle different command line parameters, some required,
some optional. A console command may also need to provide several sub-commands to handle
different sub-tasks. These work can be simplified using console command actions.
A console command action is a method in a console command class. The method name must be
of the formatactionXyz, where Xyz refers to the action name with the first letter in upper-case.
For example, a methodactionIndex defines an action named index.
To execute a specific action, we use the following console command format:
The additional option-value pairs will be passed as named parameters to the action method. The
value of axyz option will be passed as the $xyz parameter of the action method. For example, if
we define the following command class:
Then, the following console commands will all result in calling actionIndex('News', 5):
If an option is given without value (e.g. --type instead of --type=News), the corresponding
action parameter value will be assumed to be boolean true.
Note: We do not support alternative option formats such as --type News, -t News.
A parameter can take an array value by declaring it with array type hinting:
To supply the array value, we simply repeat the same option in the command line as needed:
Anonymous parameters refer to those command line parameters not in the format of options. For
example, in a command yiic sitemap index --limit=5 News, we have an anonymous
parameter whose value is Newswhile the named parameter limit is taking the value 5.
To use anonymous parameters, a command action must declare a parameter named as $args.
For example,
The $args array will hold all available anonymous parameter values.
Global options refer to those command line options that are shared by all actions in a command.
For example, in a command that provides several actions, we may want every action to
recognize an option named asverbose. While we can declare $verbose parameter in every
action method, a better way is to declare it as apublic member variable of the command class,
which turns verbose into a global option:
The above code will allow us to execute a command with a verbose option:
return array(
......
'components'=>array(
'db'=>array(
......
),
),
);
As we can see, the format of the configuration is very similar to what we do in a Web application
configuration. This is because both CConsoleApplication and CWebApplication share the same
base class.
Security
1. Cross-site Scripting Prevention
Cross-site scripting (also known as XSS) occurs when a web application gathers malicious data from a
user. Often attackers will inject JavaScript, VBScript, ActiveX, HTML, or Flash into a vulnerable
application to fool other application users and gather data from them. For example, a poorly design forum
system may display user input in forum posts without any checking. An attacker can then inject a piece of
malicious JavaScript code into a post so that when other users read this post, the JavaScript runs
unexpectedly on their computers.
One of the most important measures to prevent XSS attacks is to check user input before displaying
them. One can do HTML-encoding with the user input to achieve this goal. However, in some situations,
HTML-encoding may not be preferable because it disables all HTML tags.
Yii incorporates the work of HTMLPurifier and provides developers with a useful component
called CHtmlPurifierthat encapsulates HTMLPurifier. This component is capable of removing all malicious
code with a thoroughly audited, secure yet permissive whitelist and making sure the filtered content is
standard-compliant.
The CHtmlPurifier component can be used as either a widget or a filter. When used as a
widget, CHtmlPurifierwill purify contents displayed in its body in a view. For example,
<?php $this->beginWidget('CHtmlPurifier'); ?>
...display user-entered content here...
<?php $this->endWidget(); ?>
2. Cross-site Request Forgery Prevention
Cross-Site Request Forgery (CSRF) attacks occur when a malicious web site causes a user's web
browser to perform an unwanted action on a trusted site. For example, a malicious web site has a page
that contains an image tag whose src points to a banking site: http://bank.example/withdraw?
transfer=10000&to=someone. If a user who has a login cookie for the banking site happens to visit this
malicous page, the action of transferring 10000 dollars to someone will be executed. Contrary to cross-
site, which exploits the trust a user has for a particular site, CSRF exploits the trust that a site has for a
particular user.
To prevent CSRF attacks, it is important to abide to the rule that GET requests should only be allowed to
retrieve data rather than modify any data on the server. And for POST requests, they should include
some random value which can be recognized by the server to ensure the form is submitted from and the
result is sent back to the same origin.
Yii implements a CSRF prevention scheme to help defeat POST-based attacks. It is based on storing a
random value in a cookie and comparing this value with the value submitted via the POST request.
By default, the CSRF prevention is disabled. To enable it, configure the CHttpRequest application
component in the application configuration as follows,
return array(
'components'=>array(
'request'=>array(
'enableCsrfValidation'=>true,
),
),
);
And to display a form, call CHtml::form instead of writing the HTML form tag directly.
The CHtml::form method will embed the necessary random value in a hidden field so that it can be
submitted for CSRF validation.
3. Cookie Attack Prevention
Protecting cookies from being attacked is of extreme importance, as session IDs are commonly stored in
cookies. If one gets hold of a session ID, he essentially owns all relevant session information.
There are several countermeasures to prevent cookies from being attacked.
An application can use SSL to create a secure communication channel and only pass the
authentication cookie over an HTTPS connection. Attackers are thus unable to decipher the
contents in the transferred cookies.
Expire sessions appropriately, including all cookies and session tokens, to reduce the likelihood
of being attacked.
Prevent cross-site scripting which causes arbitrary code to run in a user's browser and expose
his cookies.
Validate cookie data and detect if they are altered.
Yii implements a cookie validation scheme that prevents cookies from being modified. In particular, it
does HMAC check for the cookie values if cookie validation is enabled.
Cookie validation is disabled by default. To enable it, configure the CHttpRequest application component
in theapplication configuration as follows,
return array(
'components'=>array(
'request'=>array(
'enableCookieValidation'=>true,
),
),
);
To make use of the cookie validation scheme provided by Yii, we also need to access cookies through
thecookies collection, instead of directly through $_COOKIES:
// retrieve the cookie with the specified name
$cookie=Yii::app()->request->cookies[$name];
$value=$cookie->value;
......
// send a cookie
$cookie=new CHttpCookie($name,$value);
Yii::app()->request->cookies[$name]=$cookie;
Performance Tuning
Performance of Web applications is affected by many factors. Database access, file system
operations, network bandwidth are all potential affecting factors. Yii has tried in every aspect to
reduce the performance impact caused by the framework. But still, there are many places in the
user application that can be improved to boost performance.
Design index wisely in a database. Indexing can make SELECT queries much faster, but it may
slow downINSERT, UPDATE or DELETE queries.
For complex queries, it is recommended to create a database view for it instead of issuing the
queries inside the PHP code and asking DBMS to parse them repetitively.
Do not overuse Active Record. Although Active Record is good at modelling data in an OOP
fashion, it actually degrades performance due to the fact that it needs to create one or several
objects to represent each row of query result. For data intensive applications, using DAO or
database APIs at lower level could be a better choice.
Last but not least, use LIMIT in your SELECT queries. This avoids fetching overwhelming data
from database and exhausting the memory allocated to PHP.
6. Minimizing Script Files
Complex pages often need to include many external JavaScript and CSS files. Because each file
would cause one extra round trip to the server and back, we should minimize the number of
script files by merging them into fewer ones. We should also consider reducing the size of each
script file to reduce the network transmission time. There are many tools around to help on these
two aspects.
For a page generated by Yii, chances are that some script files are rendered by components that
we do not want to modify (e.g. Yii core components, third-party components). In order to
minimizing these script files, we need two steps.
$cs=Yii::app()->clientScript;
$cs->scriptMap=array(
'jquery.js'=>'/js/all.js',
'jquery.ajaxqueue.js'=>'/js/all.js',
'jquery.metadata.js'=>'/js/all.js',
......
);
What the above code does is that it maps those JavaScript files to the URL /js/all.js. If any of
these JavaScript files need to be included by some components, Yii will include the URL (once)
instead of the individual script files.
Second, we need to use some tools to merge (and perhaps compress) the JavaScript files into a
single one and save it as js/all.js.
The same trick also applies to CSS files.
We can also improve page loading speed with the help of Google AJAX Libraries API. For
example, we can include jquery.js from Google servers instead of our own server. To do so, we
first configure the scriptMapas follows,
$cs=Yii::app()->clientScript;
$cs->scriptMap=array(
'jquery.js'=>false,
'jquery.ajaxqueue.js'=>false,
'jquery.metadata.js'=>false,
......
);
By mapping these script files to false, we prevent Yii from generating the code to include these
files. Instead, we write the following code in our pages to explicitly include the script files from
Google,
<head>
<?php echo CGoogleApi::init(); ?>