diff --git a/docs/guide-es/structure-controllers.md b/docs/guide-es/structure-controllers.md new file mode 100644 index 0000000000..975f07aa45 --- /dev/null +++ b/docs/guide-es/structure-controllers.md @@ -0,0 +1,448 @@ +Controladores +============= + +Los controladores son parte del patrón o arquitectura [MVC](http://es.wikipedia.org/wiki/Modelo%E2%80%93vista%E2%80%93controlador). +Son objetos que extienden de [[yii\base\Controller]] y se encargan de procesar los `requests` +generando `responses`. En particular, después de tomar el control desde las [aplicaciones](structure-applications.md), +los controladores analizarán los datos que entran en el request, los pasan a los [modelos](structure-models.md), inyectan los +modelos resultantes a las [vistas](structure-views.md), y finalmente generan los `responses` de salida. + + +## Acciones + +Los Controladores están compuestos por *acciones* que son las unidades más básicas a las que los usuarios pueden +dirigirse y solicitar ejecución. Un controlador puede tener una o múltiples acciones. + +El siguiente ejemplo muestra un controlador `post` con dos acciones: `view` y `create`: + +```php +namespace app\controllers; + +use Yii; +use app\models\Post; +use yii\web\Controller; +use yii\web\NotFoundHttpException; + +class PostController extends Controller +{ + public function actionView($id) + { + $model = Post::findOne($id); + if ($model === null) { + throw new NotFoundHttpException; + } + + return $this->render('view', [ + 'model' => $model, + ]); + } + + public function actionCreate() + { + $model = new Post; + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); + } else { + return $this->render('create', [ + 'model' => $model, + ]); + } + } +} +``` + +En la acción `view` (definida en el método `actionView()`), el código primero carga el [modelo](structure-models.md) +de acuerdo el ID del modelo solicitado; Si el modelo es cargado satisfactoriamente, lo mostrará usando una [vista](structure-views.md) +llamada `view`. Si no, arrojará una excepción. + +En la acción `create` (definida por el método `actionCreate()`), el código es similar. Primero intenta poblar +el [modelo](structure-models.md) usando datos del `request` y guardarlo. Si ambas cosas suceden correctamente, se redireccionará +el navegador a la acción `view` con el ID del modelo recientemente creado. De otro modo mostrará +la vista `create` a través de la cual el usuario puede completar los campos necesarios. + + +## Routes + +Los usuarios ejecutan las acciones a través de las llamadas *routes* (rutas). una ruta es una cadena que consiste en las siguientes partes: + +* un ID de módulo: este existe solamente si el controlador pertenece a un [módulo](structure-modules.md) que no es de la aplicación; +* un ID de controlador: una cadena que identifica exclusivamente al controlador entre todos los controladores dentro de la misma aplicación + (o el mismo módulo si el controlador pertenece a uno); +* un ID de acción: una cadena que identifica exclusivamente a la acción entre todas las acciones del mismo controlador. + +Las rutas pueden usar el siguiente formato: + +``` +ControllerID/ActionID +``` + +o el siguiente formato si el controlador pertenece a un módulo: + +```php +ModuleID/ControllerID/ActionID +``` + +Entonces si un usuario solicita la URL `http://hostname/index.php?r=site/index`, la acción `index` del controlador `site` +será ejecutado. Para más detalles acerca de cómo las son resueltas en acciones, por favor consulta +la sección [Routing](runtime-routing.md). + + +## Creando Controladores + +En [[yii\web\Application|aplicaciones Web]], los controladores deben extender de [[yii\web\Controller]] o cualquier +clase hija. De forma similar los controladores de [[yii\console\Application|aplicaciones de consola]], deben extender +de [[yii\console\Controller]] o cualquier clase hija de esta. El siguiente código define un controlador `site`: + +```php +namespace app\controllers; + +use yii\web\Controller; + +class SiteController extends Controller +{ +} +``` + + +### IDs de Controladores + +Usualmente, un controlador está diseñado para manejar los `requests` de acuerdo a un tipo de recurso. +Por esta razón, los IDs de controladores son a menudo sustantivos de los tipos de recurso que están manejando. +Por ejemplo, podrías utilizar `article` como el ID de un controlador que maneja datos de artículos. + +Por defecto, los IDs de controladores deberían contener sólo estos caracteres: letras del Inglés en minúscula, dígitos, +guiones bajos y medios, y barras. Por ejemplo, `article`, `post-comment`, `admin/post2-comment` son todos +IDs de controladores válidos, mientras que `article?`, `PostComment`, `admin\post` no lo son. + +Los guiones en un ID de controlador son utilizados para separa palabras, mientras que las barras para organizar los controladores +en sub-directorios. + + +### Nombres de Clases de Controladores + +Los nombre de clases de controladores pueden ser derivados de los IDs de acuerdo a las siguientes reglas: + +* Transforma la primera letra de cada palabra separada por guiones en mayúscula. Nota que si el ID del controlador + contiene barras, esta regla sólo aplica a la porción después de la última barra dentro del ID. +* Elimina guiones y reemplaza cualquier barra por barras invertidas. +* Agrega el sufijo `Controller`. +* Agrega al principio el [[yii\base\Application::controllerNamespace|controller namespace]]. + +Los siguientes son algunos ejemplo, asumiendo que el [[yii\base\Application::controllerNamespace|controller namespace]] +toma el valor por defecto: `app\controllers`: + +* `article` deriva en `app\controllers\ArticleController`; +* `post-comment` deriva en `app\controllers\PostCommentController`; +* `admin/post2-comment` deriva en `app\controllers\admin\Post2CommentController`. + +Las clases de controladores deben ser [autocargables](concept-autoloading.md). Por esta razón, en los ejemplos anteriores, +la clase del controlador `article` debe ser guardada en un archivo cuyo alias [alias](concept-aliases.md) +es `@app/controllers/ArticleController.php`; mientras que el controlador `admin/post2-comment` debería estar +en `@app/controllers/admin/Post2CommentController.php`. + +> Información: El último ejemplo, `admin/post2-comment`, demuestra cómo puedes poner un controlador bajo un sub-directorio + del [[yii\base\Application::controllerNamespace|controller namespace]]. Esto es útil cuando quieres organizar + tus controladores en varias categorías pero sin utilizar [módulos](structure-modules.md). + + +### Controller Map + +Puedes configurar [[yii\base\Application::controllerMap|controller map]] (mapeo de controladores) para superar las restricciones +de los IDs de controladores y sus nombres de clase descritos arriba. Esto es principalmente útil cuando estás utilizando un +controlador de terceros del cual no tiene control sobre sus nombres de clase. + +Puedes configurar [[yii\base\Application::controllerMap|controller map]] en la +[configuración de la aplicación](structure-applications.md#application-configurations) de la siguiente manera: + +```php +[ + 'controllerMap' => [ + [ + // declara el controlador "account" usando un nombre de clase + 'account' => 'app\controllers\UserController', + + // declara el controlador "article" utilizando un array de configuración + 'article' => [ + 'class' => 'app\controllers\PostController', + 'enableCsrfValidation' => false, + ], + ], + ], +] +``` + + +### Controller por Defecto + +Cada aplicación tiene un controlador por defecto especificado a través de la propiedad [[yii\base\Application::defaultRoute]]. +Cuando un `request` no especifica una [ruta](#ids-routes), se utilizará la ruta especificada en esta propiedad. +Para [[yii\web\Application|aplicaciones Web]], el valor es `'site'`, mientras que para [[yii\console\Application|aplicaciones de consola]] +es `help`. Por lo tanto, si la URL es `http://hostname/index.php`, significa que el `request` será manejado por el controlador `site`. + +Puedes cambiar el controlador por defecto con la siguiente [configuración de la aplicación](structure-applications.md#application-configurations): + +```php +[ + 'defaultRoute' => 'main', +] +``` + + +## Creando Acciones + +Crear acciones puede ser tan simple como definir un llamado *método de acción* en una clase controlador. Un método de acción es +un método *public* cuyo nombre comienza con la palabra `action`. El valor de retorno de uno de estos métodos representa +los datos de respuesta (response) a ser enviado a los usuarios. El siguiente código define dos acciones: `index` y `hello-world`: + +```php +namespace app\controllers; + +use yii\web\Controller; + +class SiteController extends Controller +{ + public function actionIndex() + { + return $this->render('index'); + } + + public function actionHelloWorld() + { + return 'Hola Mundo!'; + } +} +``` + + +### IDs de Acciones + +Una acción está a menudo diseñada para realizar una manipulación particular de un recurso. Por esta razón, +los IDs de acciones son usualmente verbos, como `view` (ver), `update` (actualizar), etc. + +Por defecto, los IDs de acciones deberían contener estos caracteres solamente: letras en Inglés en minúsculas, dígitos, +guiones bajos y barras. Los guiones en un ID de acción son utilizados para separar palabras. Por ejemplo, +`view`, `update2`, `comment-post` son todos IDs válidos, mientras que `view?` y `Update` no lo son. + +Puedes crear acciones de dos maneras: acciones en línea (inline) o acciones independientes (standalone). Una acción en línea +es definida como un método en la clase del controlador, mientras que una acción independiente es una clase que extiende +[[yii\base\Action]] o sus clases hijas. Acciones en línea son más fáciles de crear y por lo tanto preferidas +si no tienes intenciones de volver a utilizarlas. Las acciones independientes, por otro lado, son principalmente +creadas para ser reutilizadas en otros controladores o ser redistribuidas como [extensiones](structure-extensions.md). + + +### Acciones en Línea + +Acciones en línea se refieren a acciones que son definidas en términos de métodos como se acaba de describir. + +Los nombre de métodos de acciones derivan de los IDs de acuerdo al siguiente criterio: + +* Transforma la primera letra de cada palabra del ID de la acción a mayúscula; +* Elimina guiones; +* Prefija la palabra `action`. + +Por ejemplo, `index` se vuelve `actionIndex`, y `hello-world` se vuelve `actionHelloWorld`. + +> Nota: Los nombre de los métodos de acción son *case-sensitive* (distinguen entre minúsculas y mayúsculas). Si tienes un + método llamado `ActionIndex`, no será considerado como un método de acción, y como resultado, solicitar la acción `index` + resultará en una excepción. También ten en cuenta que los métodos de acción deben ser `public`. Un método `private` o `protected` + NO define un método de acción. + + +Las acciones en línea son las más comúnmente definidas ya que requieren muy poco esfuerzo de creación. De todos modos, +si planeas reutilizar la misma acción en diferentes lugares, o quieres redistribuir una acción, +deberías considerar definirla como un *acción independiente*. + + +### Acciones Independientes + +Las acciones independientes son acciones definidas en términos de clases de acción que extienden de [[yii\base\Action]] o cualquiera de sus clases hijas. +Por ejemplo, en Yii se encuentran las clases [[yii\web\ViewAction]] y [[yii\web\ErrorAction]], de las cuales ambas son acciones independientes. + +Para utilizar una acción independiente, debes declararla en el *action map* (mapeo de acciones) sobrescribiendo el método +[[yii\base\Controller::actions()]] en tu controlador de la siguiente manera: + +```php +public function actions() +{ + return [ + // declara la acción "error" utilizando un nombre de clase + 'error' => 'yii\web\ErrorAction', + + // declara la acción "view" utilizando un array de configuración + 'view' => [ + 'class' => 'yii\web\ViewAction', + 'viewPrefix' => '', + ], + ]; +} +``` + +Como puedes ver, el método `actions()` debe devolver un array cuyas claves son los IDs de acciones y sus valores los nombres +de clases de acciones o [configuraciones](concept-configurations.md). Al contrario de acciones en línea, los IDs de acciones independientes +pueden contener caracteres arbitrarios, mientras sean declarados en el método `actions()`. + + +Para crear una acción independiente, debes extender de [[yii\base\Action]] o una clase hija, e implementar un +método `public` llamado `run()`. El rol del método `run()` es similar al de un método de acción. Por ejemplo: + +```php + + +El valor de retorno de una método de acción o del método `run()` de una acción independiente son significativos. Este se refiere +al resultado de la acción correspondiente. + +El valor devuelto puede ser un objeto [response](runtime-responses.md) que será enviado como respuesta a +los usuarios. + +* Para [[yii\web\Application|aplicaciones Web]], el valor de retorno pueden ser también datos arbitrarios que serán + asignados a [[yii\web\Response::data]] y más adelante convertidos a una cadena representando el cuerpo de la respuesta. +* Para [[yii\console\Application|aplicaciones de consola], el valor de retorno puede ser también un entero representando + el [[yii\console\Response::exitStatus|status de salida]] de la ejecución del comando. + +En los ejemplos mostrados arriba, los resultados de las acciones son todas cadenas que serán tratadas como el cuerpo de la respuesta +a ser enviado a los usuarios. El siguiente ejemplo demuestra cómo una acción puede redirigir el navegador del usuario a una nueva URL +devolviendo un objeto `response` (debido a que el método [[yii\web\Controller::redirect()|redirect()]] devuelve +un objeto `response`): + +```php +public function actionForward() +{ + // redirige el navegador del usuario a http://example.com + return $this->redirect('http://example.com'); +} +``` + + +### Parámetros de Acción + +Los métodos de acción para acciones en línea y el método `run()` de acciones independientes pueden tomar parámetros, +llamados *parámetros de acción*. Sus valores son obtenidos del `request`. Para [[yii\web\Application|aplicaciones Web]], +el valor de cada parámetro de acción es tomado desde `$_GET` usando el nombre del parámetro como su clave; +para [[yii\console\Application|aplicaciones de consola]], estos corresponden a los argumentos de la línea de comandos. + +En el siguiente ejemplo, la acción `view` (una acción en línea) declara dos parámetros: `$id` y `$version`. + +```php +namespace app\controllers; + +use yii\web\Controller; + +class PostController extends Controller +{ + public function actionView($id, $version = null) + { + // ... + } +} +``` + +Los parámetros de acción serán poblados como se muestra a continuación para distintos `requests`: + +* `http://hostname/index.php?r=post/view&id=123`: el parámetro `$id` tomará el valor + `'123'`, mientras que `$version` queda como `null` debido a que no hay un parámetro `version` en la URL. +* `http://hostname/index.php?r=post/view&id=123&version=2`: los parámetros `$id` y `$version` serán llenados con + `'123'` y `'2'`, respectivamente. +* `http://hostname/index.php?r=post/view`: se lanzará una excepción [[yii\web\BadRequestHttpException]] + dado que el parámetro `$id` es requerido pero no es provisto en el `request`. +* `http://hostname/index.php?r=post/view&id[]=123`: una excepción [[yii\web\BadRequestHttpException]] será lanzada + porque el parámetro `$id` está recibiendo un valor inesperado, el array `['123']`. + +Si quieres que un parámetro de acción acepte un array como valor, deberías utilizar el `type-hinting` (especificación de tipo) `array`, +como a continuación: + +```php +public function actionView(array $id, $version = null) +{ + // ... +} +``` + +Ahora si el `request` es `http://hostname/index.php?r=post/view&id[]=123`, el parámetro `$id` tomará el valor +de `['123']`. Si el `request` es `http://hostname/index.php?r=post/view&id=123`, el parámetro `$id` recibirá aún +el mismo array como valor ya que el valor escalar `'123'` será convertido automáticamente en array. + +Los ejemplos de arriba muestran principalmente como funcionan los parámetros de acción de una aplicación Web. Para aplicaciones de consola, +por favor consulta la sección [Comandos de Consola](tutorial-console.md) para más detalles. + + +### Acción por Defecto + +Cada controlador tiene una acción por defecto especificada a través de la propiedad [[yii\base\Controller::defaultAction]]. +Cuando una [ruta](#ids-routes) contiene sólo el ID del controlador, implica que se está solicitando la acción por defecto +del controlador especificado. + +Por defecto, la acción por defecto (valga la redundancia) definida es `index`. Si quieres cambiar dicho valor, simplemente sobrescribe +esta propiedad en la clase del controlador, como se muestra a continuación: + +```php +namespace app\controllers; + +use yii\web\Controller; + +class SiteController extends Controller +{ + public $defaultAction = 'home'; + + public function actionHome() + { + return $this->render('home'); + } +} +``` + + +## Ciclo de Vida del Controlador + +Cuando se procesa un `request`, la [aplicación](structure-applications.md) creará un controlador +basada en la [ruta](#routes) solicitada. El controlador entonces irá a través del siguiente ciclo de vida +para completar el `request`: + +1. El método [[yii\base\Controller::init()]] es llamado después de que el controlador es creado y configurado. +2. El controlador crea un objecto `action` basado en el ID de acción solicitado: + * Si el ID de la acción no es especificado, el [[yii\base\Controller::defaultAction|ID de la acción por defecto]] será utilizado. + * Si el ID de la acción es encontrado en el [[yii\base\Controller::actions()|mapeo de acciones]], una acción independiente + será creada; + * Si el ID de la acción es coincide con un método de acción, una acción en línea será creada; + * De otra manera, se lanzará una excepción [[yii\base\InvalidRouteException]]. +3. El controlador llama secuencialmente al método `beforeAction()` de la aplicación, al del módulo (si el controlador + pertenece a uno) y al del controlador. + * Si alguna de las llamadas devuelve `false`, el resto de los llamados subsiguientes a `beforeAction()` serán saltados y + la ejecución de la acción será cancelada. + * Por defecto, cada llamada al método `beforeAction()` lanzará un evento `beforeAction` al cual le puedes conectar un manejador. +4. El controlador ejecuta la acción: + * Los parámetros de la acción serán analizados y poblados con los datos del `request`; +5. El controlador llama secuencialmente al método `afterAction()` del controlador, del módulo (si el controlador + pertenece a uno) y de la aplicación. + * Por defecto, cada llamada al método `afterAction()` lanzará un evento `afterAction` al cual le puedes conectar un manejador. +6. La aplicación tomará el resultado de la acción y lo asignará al [response](runtime-responses.md). + + +## Buenas Prácticas + +En una aplicación bien diseñada, los controladores son a menudo muy pequeños con cada acción conteniendo unas pocas líneas de código. +Si tu controlador se torna muy complejo, es usualmente un indicador de que deberías realizar una refactorización y mover algo de +código a otras clases. + +En resumen, los controladores + +* pueden acceder a los datos del [request](runtime-requests.md); +* puede llamar a métodos del [modelo](structure-models.md) y otros componentes con data del `request`; +* pueden utilizar [vistas](structure-views.md) para componer `responses`; +* NO debe procesar datos del `request - esto debe ser realizado en los [modelos](structure-models.md); +* deberían evitar insertar HTML o cualquier código de presentación - para esto están las [vistas](structure-views.md).