Paul Klimov
10 years ago
146 changed files with 11007 additions and 830 deletions
@ -0,0 +1,291 @@
|
||||
Eventos |
||||
======= |
||||
|
||||
Los eventos permiten inyectar código dentro de otro código existente en ciertos puntos de ejecución. Se pueden adjuntar |
||||
código personalizado a un evento, cuando se lance (triggered), el código se ejecutará automáticamente. Por ejemplo, un |
||||
objeto `mailer` puede lanzar el evento `messageSent` cuando se envía un mensaje correctamente. Si se quiere rastrear |
||||
el correcto envío del mensaje, se puede, simplemente, añadir un código de seguimiento al evento `messageSent`. |
||||
|
||||
Yii introduce una clase base [[yii\base\Component]] para soportar eventos. Si una clase necesita lanzar un evento, |
||||
este debe extender a [[yii\base\Component]] o a una clase hija. |
||||
|
||||
Gestor de Eventos <a name="event-handlers"></a> |
||||
----------------- |
||||
|
||||
Un gestor de eventos es una |
||||
[llamada de retorno PHP (PHP callback)](http://php.net/manual/es/language.types.callable.php) que se ejecuta cuando se |
||||
lanza el evento al que corresponde. Se puede usar cualquier llamada de retorno de las enumeradas a continuación: |
||||
|
||||
- una función de PHP global especificada como una cadena de texto (sin paréntesis), p. ej. `'trim'`; |
||||
- un método de objeto especificado como un array de un objeto y un nombre de método como una cadena de texto |
||||
(sin paréntesis), p. ej. `[$object, 'methodNAme']`; |
||||
- un método de clase estático especificado como un array de un nombre de clase y un método como una cadena de texto |
||||
(sin paréntesis), p. ej. `[$class, 'methodName']`; |
||||
- una función anónima, p. ej. `function ($event) { ... }`. |
||||
|
||||
La firma de un gestor de eventos es: |
||||
|
||||
```php |
||||
function ($event) { |
||||
// $event es un objeto de yii\base\Event o de una clase hija |
||||
} |
||||
``` |
||||
|
||||
Un gestor de eventos puede obtener la siguiente información acerca de un evento ya sucedido mediante el parámetro |
||||
`$event`: |
||||
|
||||
- [[yii\base\Event::name|event name]] |
||||
- [[yii\base\Event::sender|event sender]]: el objeto desde el que se ha ejecutado `trigger()` |
||||
- [[yii\base\Event::data|custom data]]: los datos que se proporcionan al adjuntar el gestor de eventos |
||||
(se explicará más adelante) |
||||
|
||||
Añadir Gestores de Eventos <a name="attaching-event-handlers"></a> |
||||
-------------------------- |
||||
|
||||
Se puede añadir un gestor a un evento llamando al método [[yii\base\Component::on()]]. Por ejemplo: |
||||
|
||||
```php |
||||
$foo = new Foo; |
||||
|
||||
// este gestor es una función global |
||||
$foo->on(Foo::EVENT_HELLO, 'function_name'); |
||||
|
||||
// este gestor es un método de objeto |
||||
$foo->on(Foo::EVENT_HELLO, [$object, 'methodName']); |
||||
|
||||
// este gestor es un método de clase estática |
||||
$foo->on(Foo::EVENT_HELLO, ['app\components\Bar', 'methodName']); |
||||
|
||||
// este gestor es una función anónima |
||||
$foo->on(Foo::EVENT_HELLO, function ($event) { |
||||
// event handling logic |
||||
}); |
||||
``` |
||||
|
||||
También se pueden adjuntar gestores de eventos mediante [configuraciones](concept-configurations.md). Se pueden |
||||
encontrar más de talles en la sección [Configuraciones](concept-configurations.md#configuration-format). |
||||
|
||||
Cuando se adjunta un gestor de eventos, se pueden proporcionar datos adicionales como tercer parámetro de |
||||
[[yii\base\Component::on()]]. El gestor podrá acceder a los datos cuando se lance el evento y se ejecute el gestor. |
||||
Por ejemplo: |
||||
|
||||
```php |
||||
// El siguiente código muestra "abc" cuando se lanza el evento |
||||
// ya que $event->data contiene los datos enviados en el tercer parámetro de "on" |
||||
$foo->on(Foo::EVENT_HELLO, 'function_name', 'abc'); |
||||
|
||||
function function_name($event) { |
||||
echo $event->data; |
||||
} |
||||
``` |
||||
|
||||
Ordenación de Gestores de Eventos |
||||
--------------------------------- |
||||
|
||||
Se puede adjuntar uno o más gestores a un único evento. Cuando se lanza un evento, se ejecutarán los gestores adjuntos |
||||
en el orden que se hayan añadido al evento. Si un gestor necesita parar la invocación de los gestores que le siguen, |
||||
se puede establecer la propiedad [[yii\base\Event::handled]] del parámetro `$event` para que sea `true`: |
||||
|
||||
```php |
||||
$foo->on(Foo::EVENT_HELLO, function ($event) { |
||||
$event->handled = true; |
||||
}); |
||||
``` |
||||
|
||||
De forma predeterminada, cada nuevo gestor añadido se pone a la cola de la lista de gestores del evento. Por lo tanto, |
||||
el gestor se ejecutará en el último lugar cuando se lance el evento. Para insertar un nuevo gestor al principio de la |
||||
cola de gestores para que sea ejecutado primero, se debe llamar a [[yii\base\Component::on()]], pasando al cuarto |
||||
parámetro `$append` el valor `false`: |
||||
|
||||
```php |
||||
$foo->on(Foo::EVENT_HELLO, function ($event) { |
||||
// ... |
||||
}, $data, false); |
||||
``` |
||||
|
||||
Lanzamiento de Eventos <a name="triggering-events"></a> |
||||
---------------------- |
||||
|
||||
Los eventos se lanzan llamando al método [[yii\base\Component::trigger()]]. El método requiere un *nombre de evento*, |
||||
y de forma opcional un objeto de evento que describa los parámetros que se enviarán a los gestores de eventos. Por |
||||
ejemplo: |
||||
|
||||
```php |
||||
namespace app\components; |
||||
|
||||
use yii\base\Component; |
||||
use yii\base\Event; |
||||
|
||||
class Foo extends Component |
||||
{ |
||||
const EVENT_HELLO = 'hello'; |
||||
|
||||
public function bar() |
||||
{ |
||||
$this->trigger(self::EVENT_HELLO); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Con el código anterior, cada llamada a `bar()` lanzará un evento llamado `hello` |
||||
|
||||
> Consejo: Se recomienda usar las constantes de clase para representar nombres de eventos. En el anterior ejemplo, la |
||||
constante `EVENT_HELLO` representa el evento `hello`. Este enfoque proporciona tres beneficios. Primero, previene |
||||
errores tipográficos. Segundo, puede hacer que los IDEs reconozcan los eventos en las funciones de auto-completado. |
||||
Tercero, se puede ver que eventos soporta una clase simplemente revisando la declaración de constantes. |
||||
|
||||
A veces cuando se lanza un evento se puede querer pasar información adicional al gestor de eventos. Por ejemplo, un |
||||
`mailer` puede querer enviar la información del mensaje para que los gestores del evento `messageSent` para que los |
||||
gestores puedan saber las particularidades del mensaje enviado. Para hacerlo, se puede proporcionar un objeto de tipo |
||||
evento como segundo parámetro al método [[yii\base\Component::trigger()]]. El objeto de tipo evento debe ser una |
||||
instancia de la clase [[yii\base\Event]] o de sus hijas. Por ejemplo: |
||||
|
||||
```php |
||||
namespace app\components; |
||||
|
||||
use yii\base\Component; |
||||
use yii\base\Event; |
||||
|
||||
class MessageEvent extends Event |
||||
{ |
||||
public $message; |
||||
} |
||||
|
||||
class Mailer extends Component |
||||
{ |
||||
const EVENT_MESSAGE_SENT = 'messageSent'; |
||||
|
||||
public function send($message) |
||||
{ |
||||
// ...enviando $message... |
||||
|
||||
$event = new MessageEvent; |
||||
$event->message = $message; |
||||
$this->trigger(self::EVENT_MESSAGE_SENT, $event); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Cuando se lanza el método [[yii\base\Component::trigger()]], se ejecutarán todos los gestores adjuntos al evento. |
||||
|
||||
Desadjuntar Gestores de Evento <a name="detaching-event-handlers"></a> |
||||
------------------------------ |
||||
|
||||
Para desadjuntar un gestor de un evento, se puede ejecutar el método [[yii\base\Component::off()]]. Por ejemplo: |
||||
|
||||
```php |
||||
// el gestor es una función global |
||||
$foo->off(Foo::EVENT_HELLO, 'function_name'); |
||||
|
||||
// el gestor es un método de objeto |
||||
$foo->off(Foo::EVENT_HELLO, [$object, 'methodName']); |
||||
|
||||
// el gestor es un método estático de clase |
||||
$foo->off(Foo::EVENT_HELLO, ['app\components\Bar', 'methodName']); |
||||
|
||||
// el gestor es una función anónima |
||||
$foo->off(Foo::EVENT_HELLO, $anonymousFunction); |
||||
``` |
||||
|
||||
Tenga en cuenta que en general no se debe intentar desadjuntar las funciones anónimas a no ser que se almacene donde |
||||
se ha adjuntado al evento. En el anterior ejemplo, se asume que la función anónima se almacena como variable |
||||
`$anonymousFunction`. |
||||
|
||||
Para desadjuntar TODOS los gestores de un evento, se puede llamar [[yii\base\Component::off()]] sin el segundo |
||||
parámetro: |
||||
|
||||
```php |
||||
$foo->off(Foo::EVENT_HELLO); |
||||
``` |
||||
|
||||
Nivel de Clase (Class-Level) Gestores de Eventos <a name="class-level-event-handlers"></a> |
||||
------------------------------------------------ |
||||
|
||||
En las subsecciones anteriores se ha descrito como adjuntar un gestor a un evento a *nivel de instancia*. A veces, se |
||||
puede querer que un gestor responda todos los eventos de *todos* las instancias de una clase en lugar de una instancia |
||||
especifica. En lugar de adjuntar un gestor de eventos a una instancia, se puede adjuntar un gestor a *nivel de clase* |
||||
llamando al método estático [[yii\base\Event::on()]]. |
||||
|
||||
Por ejemplo, un objeto de tipo [Active Record](db-active-record.md) lanzará un evento |
||||
[[yii\db\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] cada vez que inserte un nuevo registro en la base |
||||
de datos. Para poder registrar las inserciones efectuadas por *todos* los objetos |
||||
[Active Record](db-active-record.md), se puede usar el siguiente código: |
||||
|
||||
```php |
||||
use Yii; |
||||
use yii\base\Event; |
||||
use yii\db\ActiveRecord; |
||||
|
||||
Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) { |
||||
Yii::trace(get_class($event->sender) . ' is inserted'); |
||||
}); |
||||
``` |
||||
|
||||
Se invocará al gestor de eventos cada vez que una instancia de [[yii\db\ActiveRecord|ActiveRecord]], o de uno de sus |
||||
clases hijas, lance un evento de tipo [[yii\db\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]]. Se puede |
||||
obtener el objeto que ha lanzado el evento mediante `$event->sender` en el gestor. |
||||
|
||||
Cuando un objeto lanza un evento, primero llamará los gestores a nivel de instancia, y a continuación los gestores a |
||||
nivel de clase. |
||||
|
||||
Se puede lanzar un evento de tipo *nivel de clase* llamando al método estático [[yii\base\Event::trigger()]]. Un |
||||
evento de nivel de clase no se asocia a un objeto en particular. Como resultado, esto provocará solamente la |
||||
invocación de los gestores de eventos a nivel de clase. |
||||
|
||||
```php |
||||
use yii\base\Event; |
||||
|
||||
Event::on(Foo::className(), Foo::EVENT_HELLO, function ($event) { |
||||
echo $event->sender; // displays "app\models\Foo" |
||||
}); |
||||
|
||||
Event::trigger(Foo::className(), Foo::EVENT_HELLO); |
||||
``` |
||||
|
||||
Tenga en cuenta que en este caso, el `$event->sender` hace referencia al nombre de la clase que lanza el evento en |
||||
lugar de a la instancia del objeto. |
||||
|
||||
> Nota: Debido a que los gestores a nivel de clase responderán a los eventos lanzados por cualquier instancia de la |
||||
clase, o cualquier clase hija, se debe usar con cuidado, especialmente en las clases de bajo nivel (low-level), tales |
||||
como [[yii\base\Object]]. |
||||
|
||||
Para desadjuntar un gestor de eventos a nivel de clase, se tiene que llamar a [[yii\base\Event::off()]]. Por ejemplo: |
||||
|
||||
```php |
||||
// desadjunta $handler |
||||
Event::off(Foo::className(), Foo::EVENT_HELLO, $handler); |
||||
|
||||
// desadjunta todos los gestores de Foo::EVENT_HELLO |
||||
Event::off(Foo::className(), Foo::EVENT_HELLO); |
||||
``` |
||||
|
||||
Eventos Globales <a name="global-events"></a> |
||||
---------------- |
||||
|
||||
Yii soporta los llamados *eventos globales*, que en realidad es un truco basado en el gestor de eventos descrito |
||||
anteriormente. El evento global requiere un Singleton globalmente accesible, tal como la instancia de |
||||
[aplicación](structure-applications.md) en si misma. |
||||
|
||||
Para crear un evento global, un evento remitente (event sender) llama al método `trigger()` del Singleton para lanzar |
||||
el evento, en lugar de llamar al propio método `trigger()` del remitente. De forma similar, los gestores de eventos se |
||||
adjuntan al evento del Singleton. Por ejemplo: |
||||
|
||||
```php |
||||
use Yii; |
||||
use yii\base\Event; |
||||
use app\components\Foo; |
||||
|
||||
Yii::$app->on('bar', function ($event) { |
||||
echo get_class($event->sender); // muestra "app\components\Foo" |
||||
}); |
||||
|
||||
Yii::$app->trigger('bar', new Event(['sender' => new Foo])); |
||||
``` |
||||
|
||||
Un beneficio de usar eventos globales es que no se necesita un objeto cuando se adjuntan gestores a un evento para que |
||||
sean lanzados por el objeto. En su lugar, los gestores adjuntos y el lanzamiento de eventos se efectúan en el |
||||
Singleton (p. ej. la instancia de la aplicación). |
||||
|
||||
Sin embargo, debido a que los `namespaces` de los eventos globales son compartidos por todas partes, se les deben |
||||
asignar nombres bien pensados, como puede ser la introducción de algún `namespace` |
||||
(p. ej. "frontend.mail.sent", "backend.mail.sent"). |
@ -0,0 +1,470 @@
|
||||
Constructor de Consultas |
||||
======================== |
||||
|
||||
> Nota: Esta sección está en desarrollo. |
||||
|
||||
Yii proporciona una capa de acceso básico a bases de datos como se describe en la sección |
||||
[Objetos de Acceso a Bases de Datos](db-dao.md). La capa de acceso a bases de datos proporciona un método de bajo |
||||
nivel (low-level) para interaccionar con la base de datos. Aunque a veces puede ser útil la escritura de sentencias |
||||
SQLs puras, en otras situaciones puede ser pesado y propenso a errores. Otra manera de tratar con bases de datos puede |
||||
ser el uso de Constructores de Consultas (Query Builder). El Constructor de Consultas proporciona un medio orientado a |
||||
objetos para generar las consultas que se ejecutarán. |
||||
|
||||
Un uso típico de Constructor de Consultas puede ser el siguiente: |
||||
|
||||
```php |
||||
$rows = (new \yii\db\Query()) |
||||
->select('id, name') |
||||
->from('user') |
||||
->limit(10) |
||||
->all(); |
||||
|
||||
// que es equivalente al siguiente código: |
||||
|
||||
$query = (new \yii\db\Query()) |
||||
->select('id, name') |
||||
->from('user') |
||||
->limit(10); |
||||
|
||||
// Crear un comando. Se puede obtener la consulta SQL actual utilizando $command->sql |
||||
$command = $query->createCommand(); |
||||
|
||||
// Ejecutar el comando: |
||||
$rows = $command->queryAll(); |
||||
``` |
||||
|
||||
Métodos de Consulta |
||||
------------------- |
||||
|
||||
Como se puede observar, primero se debe tratar con [[yii\db\Query]]. En realidad, `Query` sólo se encarga de |
||||
representar diversa información de la consulta. La lógica para generar la consulta se efectúa mediante |
||||
[[yii\db\QueryBuilder]] cuando se llama al método `createCommand()`, y la ejecución de la consulta la efectúa |
||||
[[yii\db\Command]]. |
||||
|
||||
Se ha establecido, por convenio, que [[yii\db\Query]] proporcione un conjunto de métodos de consulta comunes que |
||||
construirán la consulta, la ejecutarán, y devolverán el resultado. Por ejemplo: |
||||
|
||||
- [[yii\db\Query::all()|all()]]: construye la consulta, la ejecuta y devuelve todos los resultados en formato de array. |
||||
- [[yii\db\Query::one()|one()]]: devuelve la primera fila del resultado. |
||||
- [[yii\db\Query::column()|column()]]: devuelve la primera columna del resultado. |
||||
- [[yii\db\Query::scalar()|scalar()]]: devuelve la primera columna en la primera fila del resultado. |
||||
- [[yii\db\Query::exists()|exists()]]: devuelve un valor indicando si la el resultado devuelve algo. |
||||
- [[yii\db\Query::count()|count()]]: devuelve el resultado de la consulta `COUNT`. Otros métodos similares incluidos |
||||
son `sum($q)`, `average($q)`, `max($q)`, `min($q)`, que soportan las llamadas funciones de agregación. El parámetro |
||||
`$q` es obligatorio en estos métodos y puede ser el nombre de la columna o expresión. |
||||
|
||||
Construcción de Consultas |
||||
------------------------- |
||||
|
||||
A continuación se explicará como construir una sentencia SQL que incluya varias clausulas. Para simplificarlo, usamos |
||||
`$query` para representar el objeto [[yii\db\Query]]: |
||||
|
||||
### `SELECT` |
||||
|
||||
Para formar una consulta `SELECT` básica, se necesita especificar que columnas y de que tablas se seleccionarán: |
||||
|
||||
```php |
||||
$query->select('id, name') |
||||
->from('user'); |
||||
``` |
||||
|
||||
Las opciones de select se pueden especificar como una cadena de texto (string) separada por comas o como un array. La |
||||
sintaxis del array es especialmente útil cuando se forma la selección dinámicamente. |
||||
|
||||
```php |
||||
$query->select(['id', 'name']) |
||||
->from('user'); |
||||
``` |
||||
|
||||
> Información: Se debe usar siempre el formato array si la clausula `SELECT` contiene expresiones SQL. Esto se debe a |
||||
que una expresión SQL como `CONCAT(first_name, last_name) AS full_name` puede contener comas. Si se junta con otra |
||||
cadena de texto de otra columna, puede ser que la expresión se divida en varias partes por comas, esto puede |
||||
conllevar a errores. |
||||
|
||||
Cuando se especifican columnas, se pueden incluir los prefijos de las tablas o alias de columnas, p. ej. `user.id`, |
||||
`user.id AS user_id`. Si se usa un array para especificar las columnas, también se pueden usar las claves del array |
||||
para especificar los alias de columna, p. ej. `['user_id' => 'user.id', 'user_name' => 'user.name']`. |
||||
|
||||
A partir de la versión 2.0.1, también se pueden seleccionar subconsultas como columnas. Por ejemplo: |
||||
|
||||
```php |
||||
$subQuery = (new Query)->select('COUNT(*)')->from('user'); |
||||
$query = (new Query)->select(['id', 'count' => $subQuery])->from('post'); |
||||
// $query representa la siguiente sentencia SQL: |
||||
// SELECT `id`, (SELECT COUNT(*) FROM `user`) AS `count` FROM `post` |
||||
``` |
||||
|
||||
Para seleccionar filas distintas, se puede llamar a `distinct()`, como se muestra a continuación: |
||||
|
||||
```php |
||||
$query->select('user_id')->distinct()->from('post'); |
||||
``` |
||||
|
||||
### `FROM` |
||||
|
||||
Para especificar de que tabla(s) se quieren seleccionar los datos, se llama a `from()`: |
||||
|
||||
```php |
||||
$query->select('*')->from('user'); |
||||
``` |
||||
|
||||
Se pueden especificar múltiples tablas usando una cadena de texto separado por comas o un array. Los nombres de tablas |
||||
pueden contener prefijos de esquema (p. ej. `'public.user'`) y/o alias de tablas (p. ej. `'user u'). El método |
||||
entrecomillara automáticamente los nombres de tablas a menos que contengan algún paréntesis (que significa que se |
||||
proporciona la tabla como una subconsulta o una expresión de BD). Por ejemplo: |
||||
|
||||
```php |
||||
$query->select('u.*, p.*')->from(['user u', 'post p']); |
||||
``` |
||||
|
||||
Cuando se especifican las tablas como un array, también se pueden usar las claves de los arrays como alias de tablas |
||||
(si una tabla no necesita alias, no se usa una clave en formato texto). Por ejemplo: |
||||
|
||||
```php |
||||
$query->select('u.*, p.*')->from(['u' => 'user', 'p' => 'post']); |
||||
``` |
||||
|
||||
Se puede especificar una subconsulta usando un objeto `Query`. En este caso, la clave del array correspondiente se |
||||
usará como alias para la subconsulta. |
||||
|
||||
```php |
||||
$subQuery = (new Query())->select('id')->from('user')->where('status=1'); |
||||
$query->select('*')->from(['u' => $subQuery]); |
||||
``` |
||||
|
||||
### `WHERE` |
||||
|
||||
Habitualmente se seleccionan los datos basándose en ciertos criterios. El Constructor de Consultas tiene algunos |
||||
métodos útiles para especificarlos, el más poderoso de estos es `where`, y se puede usar de múltiples formas. |
||||
|
||||
La manera más simple para aplicar una condición es usar una cadena de texto: |
||||
|
||||
```php |
||||
$query->where('status=:status', [':status' => $status]); |
||||
``` |
||||
|
||||
Cuando se usan cadenas de texto, hay que asegurarse que se unen los parámetros de la consulta, no crear una consulta |
||||
mediante concatenación de cadenas de texto. El enfoque anterior es seguro, el que se muestra a continuación, no lo es: |
||||
|
||||
```php |
||||
$query->where("status=$status"); // Peligroso! |
||||
``` |
||||
|
||||
En lugar de enlazar los valores de estado inmediatamente, se puede hacer usando `params` o `addParams`: |
||||
|
||||
```php |
||||
$query->where('status=:status'); |
||||
$query->addParams([':status' => $status]); |
||||
``` |
||||
|
||||
Se pueden establecer múltiples condiciones en `where` usando el *formato hash*. |
||||
|
||||
```php |
||||
$query->where([ |
||||
'status' => 10, |
||||
'type' => 2, |
||||
'id' => [4, 8, 15, 16, 23, 42], |
||||
]); |
||||
``` |
||||
|
||||
El código generará la el siguiente SQL: |
||||
|
||||
```sql |
||||
WHERE (`status` = 10) AND (`type` = 2) AND (`id` IN (4, 8, 15, 16, 23, 42)) |
||||
``` |
||||
|
||||
El valor NULO es un valor especial en las bases de datos, y el Constructor de Consultas lo gestiona inteligentemente. |
||||
Este código: |
||||
|
||||
```php |
||||
$query->where(['status' => null]); |
||||
``` |
||||
|
||||
da como resultado la siguiente cláusula WHERE: |
||||
|
||||
```sql |
||||
WHERE (`status` IS NULL) |
||||
``` |
||||
|
||||
También se pueden crear subconsultas con objetos de tipo `Query` como en el siguiente ejemplo: |
||||
|
||||
```php |
||||
$userQuery = (new Query)->select('id')->from('user'); |
||||
$query->where(['id' => $userQuery]); |
||||
``` |
||||
|
||||
que generará el siguiente código SQL: |
||||
|
||||
```sql |
||||
WHERE `id` IN (SELECT `id` FROM `user`) |
||||
``` |
||||
|
||||
Otra manera de usar el método es el formato de operando que es `[operator, operand1, operand2, ...]`. |
||||
|
||||
El operando puede ser uno de los siguientes (ver también [[yii\db\QueryInterface::where()]]): |
||||
|
||||
- `and`: los operandos deben concatenerase usando `AND`. por ejemplo, `['and', 'id=1', 'id=2']` generará |
||||
`id=1 AND id=2`. Si el operando es un array, se convertirá en una cadena de texto usando las reglas aquí descritas. |
||||
Por ejemplo, `['and', 'type=1', ['or', 'id=1', 'id=2']]` generará `type=1 AND (id=1 OR id=2)`. El método no |
||||
ejecutará ningún filtrado ni entrecomillado. |
||||
|
||||
- `or`: similar al operando `and` exceptuando que los operando son concatenados usando `OR`. |
||||
|
||||
- `between`: el operando 1 debe ser el nombre de columna, y los operandos 2 y 3 deben ser los valores iniciales y |
||||
finales del rango en el que se encuentra la columna. Por ejemplo, `['between', 'id', 1, 10]` generará |
||||
`id BETWEEN 1 AND 10`. |
||||
|
||||
- `not between`: similar a `between` exceptuando que `BETWEEN` se reemplaza por `NOT BETWEEN` en la condición |
||||
generada. |
||||
|
||||
- `in`: el operando 1 debe ser una columna o una expresión de BD. El operando 2 puede ser un array o un objeto de tipo |
||||
`Query`. Generará una condición `IN`. Si el operando 2 es un array, representará el rango de valores que puede |
||||
albergar la columna o la expresión de BD; Si el operando 2 es un objeto de tipo `Query`, se generará una subconsulta |
||||
y se usará como rango de la columna o de la expresión de BD. Por ejemplo, `['in', 'id', [1, 2, 3]]` generará |
||||
`id IN (1, 2, 3)`. El método entrecomillará adecuadamente el nombre de columna y filtrará los valores del rango. El |
||||
operando `in` también soporta columnas compuestas. En este caso, el operando 1 debe se un array de columnas, |
||||
mientras que el operando 2 debe ser un array de arrays o un objeto de tipo `Query` que represente el rango de las |
||||
columnas. |
||||
|
||||
- `not in`: similar que el operando `in` exceptuando que `IN` se reemplaza por `NOT IN` en la condición generada. |
||||
|
||||
- `like`: el operando 1 debe ser una columna o una expresión de BD, y el operando 2 debe ser una cadena de texto o un |
||||
array que represente los valores a los que tienen que asemejarse la columna o la expresión de BD.Por ejemplo, |
||||
`['like', 'name', 'tester']` generará `name LIKE '%tester%'`. Cuando se da el valor rango como un array, se |
||||
generarán múltiples predicados `LIKE` y se concatenaran usando `AND`. Por ejemplo, |
||||
`['like', 'name', ['test', 'sample']]` generará `name LIKE '%test%' AND name LIKE '%sample%'`. También se puede |
||||
proporcionar un tercer operando opcional para especificar como deben filtrarse los caracteres especiales en los |
||||
valores. El operando debe se un array que mapeen los caracteres especiales a sus caracteres filtrados asociados. Si |
||||
no se proporciona este operando, se aplicará el mapeo de filtrado predeterminado. Se puede usar `false` o un array |
||||
vacío para indicar que los valores ya están filtrados y no se necesita aplicar ningún filtro. Hay que tener en |
||||
cuenta que cuando se usa un el mapeo de filtrado (o no se especifica el tercer operando), los valores se encerraran |
||||
automáticamente entre un par de caracteres de porcentaje. |
||||
|
||||
> Nota: Cuando se usa PostgreSQL también se puede usar |
||||
[`ilike`](http://www.postgresql.org/docs/8.3/static/functions-matching.html#FUNCTIONS-LIKE) en lugar de `like` para |
||||
filtrar resultados insensibles a mayúsculas (case-insensitive). |
||||
|
||||
- `or like`: similar al operando `like` exceptuando que se usa `OR` para concatenar los predicados `LIKE` cuando haya |
||||
un segundo operando en un array. |
||||
|
||||
- `not like`: similar al operando `like` exceptuando que se usa `LIKE` en lugar de `NOT LIKE` en las condiciones |
||||
generadas. |
||||
|
||||
- `or not like`: similar al operando `not like` exceptuando que se usa `OR` para concatenar los predicados `NOT LIKE`. |
||||
|
||||
- `exists`: requiere un operando que debe ser una instancia de [[yii\db\Query]] que represente la subconsulta. Esto |
||||
generará una expresión `EXISTS (sub-query)`. |
||||
|
||||
- `not exists`: similar al operando `exists` y genera una expresión `NOT EXISTS (sub-query)`. |
||||
|
||||
Adicionalmente se puede especificar cualquier cosa como operando: |
||||
|
||||
```php |
||||
$query->select('id') |
||||
->from('user') |
||||
->where(['>=', 'id', 10]); |
||||
``` |
||||
|
||||
Cuyo resultado será: |
||||
|
||||
```sql |
||||
SELECT id FROM user WHERE id >= 10; |
||||
``` |
||||
|
||||
Si se construyen partes de una condición dinámicamente, es muy convenientes usar `andWhere()` y `orWhere()`: |
||||
|
||||
```php |
||||
$status = 10; |
||||
$search = 'yii'; |
||||
|
||||
$query->where(['status' => $status]); |
||||
if (!empty($search)) { |
||||
$query->andWhere(['like', 'title', $search]); |
||||
} |
||||
``` |
||||
|
||||
En el caso que `$search` no este vacío, se generará el siguiente código SQL: |
||||
|
||||
```sql |
||||
WHERE (`status` = 10) AND (`title` LIKE '%yii%') |
||||
``` |
||||
|
||||
#### Construcción de Condiciones de Filtro |
||||
|
||||
Cuando se generan condiciones de filtro basadas en datos recibidos de usuarios (inputs), a menudo se quieren gestionar |
||||
de forma especial las "datos vacíos" para ignorarlos en los filtros. Por ejemplo, teniendo un formulario HTML que |
||||
obtiene el nombre de usuario y la dirección de correo electrónico. Si el usuario solo rellena el campo de nombre de |
||||
usuario, se puede querer generar una consulta para saber si el nombre de usuario recibido es valido. Se puede usar |
||||
`filterWhere()` para conseguirlo: |
||||
|
||||
```php |
||||
// $username y $email son campos de formulario rellenados por usuarios |
||||
$query->filterWhere([ |
||||
'username' => $username, |
||||
'email' => $email, |
||||
]); |
||||
``` |
||||
|
||||
El método `filterWhere()` es muy similar al método `where()`. La principal diferencia es que el `filterWhere()` |
||||
eliminará los valores vacíos de las condiciones proporcionadas. Por lo tanto si `$email` es "vació", la consulta |
||||
resultante será `...WHERE username=:username`; y si tanto `$username` como `$email` son "vacías", la consulta no |
||||
tendrá `WHERE`. |
||||
|
||||
Decimos que un valor es *vacío* si es nulo, una cadena de texto vacía, una cadena de texto que consista en espacios en |
||||
blanco o un array vacío. |
||||
|
||||
También se pueden usar `andFilterWhere()` y `orFilterWhere()` para añadir más condiciones de filtro. |
||||
|
||||
### `ORDER BY` |
||||
|
||||
Se pueden usar `orderBy` y `addOrderBy` para ordenar resultados: |
||||
|
||||
```php |
||||
$query->orderBy([ |
||||
'id' => SORT_ASC, |
||||
'name' => SORT_DESC, |
||||
]); |
||||
``` |
||||
|
||||
Aquí estamos ordenando por `id` ascendente y después por `name` descendente. |
||||
|
||||
### `GROUP BY` and `HAVING` |
||||
|
||||
Para añadir `GROUP BY` al SQL generado se puede usar el siguiente código: |
||||
|
||||
```php |
||||
$query->groupBy('id, status'); |
||||
``` |
||||
|
||||
Si se quieren añadir otro campo después de usar `groupBy`: |
||||
|
||||
```php |
||||
$query->addGroupBy(['created_at', 'updated_at']); |
||||
``` |
||||
|
||||
Para añadir la condición `HAVING` se pueden usar los métodos `having` y `andHaving` y `orHaving`. Los parámetros para |
||||
ellos son similares a los del grupo de métodos `where`: |
||||
|
||||
```php |
||||
$query->having(['status' => $status]); |
||||
``` |
||||
|
||||
### `LIMIT` and `OFFSET` |
||||
|
||||
Para limitar el resultado a 10 filas se puede usar `limit`: |
||||
|
||||
```php |
||||
$query->limit(10); |
||||
``` |
||||
|
||||
Para saltarse las 100 primeras filas, se puede usar: |
||||
|
||||
```php |
||||
$query->offset(100); |
||||
``` |
||||
|
||||
### `JOIN` |
||||
|
||||
Las clausulas `JOIN` se generan en el Constructor de Consultas usando el método join aplicable: |
||||
|
||||
- `innerJoin()` |
||||
- `leftJoin()` |
||||
- `rightJoin()` |
||||
|
||||
Este left join selecciona los datos desde dos tablas relacionadas en una consulta: |
||||
|
||||
```php |
||||
$query->select(['user.name AS author', 'post.title as title']) |
||||
->from('user') |
||||
->leftJoin('post', 'post.user_id = user.id'); |
||||
``` |
||||
|
||||
En el código, el primer parámetro del método `leftjoin` especifica la tabla a la que aplicar el join. El segundo |
||||
parámetro, define la condición del join. |
||||
|
||||
Si la aplicación de bases de datos soporta otros tipos de joins, se pueden usar mediante el método `join` genérico: |
||||
|
||||
```php |
||||
$query->join('FULL OUTER JOIN', 'post', 'post.user_id = user.id'); |
||||
``` |
||||
|
||||
El primer argumento es el tipo de join a realizar. El segundo es la tabla a la que aplicar el join, y el tercero es la condición: |
||||
|
||||
Como en `FROM`, también se pueden efectuar joins con subconsultas. Para hacerlo, se debe especificar la subconsulta |
||||
como un array que tiene que contener un elemento. El valor del array tiene que ser un objeto de tipo `Query` que |
||||
represente la subconsulta, mientras que la clave del array es el alias de la subconsulta. Por ejemplo: |
||||
|
||||
```php |
||||
$query->leftJoin(['u' => $subQuery], 'u.id=author_id'); |
||||
``` |
||||
|
||||
### `UNION` |
||||
|
||||
En SQL `UNION` agrega resultados de una consulta a otra consulta. Las columnas devueltas por ambas consultas deben |
||||
coincidir. En Yii para construirla, primero se pueden formar dos objetos de tipo query y después usar el método |
||||
`union`: |
||||
|
||||
```php |
||||
$query = new Query(); |
||||
$query->select("id, category_id as type, name")->from('post')->limit(10); |
||||
|
||||
$anotherQuery = new Query(); |
||||
$anotherQuery->select('id, type, name')->from('user')->limit(10); |
||||
|
||||
$query->union($anotherQuery); |
||||
``` |
||||
|
||||
Consulta por Lotes |
||||
--------------- |
||||
|
||||
Cuando se trabaja con grandes cantidades de datos, los métodos como [[yii\db\Query::all()]] no son adecuados ya que |
||||
requieren la carga de todos los datos en memoria. Para mantener los requerimientos de memoria reducidos, Yii |
||||
proporciona soporte a las llamadas consultas por lotes (batch query). Una consulta por lotes usa un cursor de datos y |
||||
recupera los datos en bloques. |
||||
|
||||
Las consultas por lotes se pueden usar del siguiente modo: |
||||
|
||||
```php |
||||
use yii\db\Query; |
||||
|
||||
$query = (new Query()) |
||||
->from('user') |
||||
->orderBy('id'); |
||||
|
||||
foreach ($query->batch() as $users) { |
||||
// $users is an array of 100 or fewer rows from the user table |
||||
} |
||||
|
||||
// o si se quieren iterar las filas una a una |
||||
foreach ($query->each() as $user) { |
||||
// $user representa uno fila de datos de la tabla user |
||||
} |
||||
``` |
||||
|
||||
Los métodos [[yii\db\Query::batch()]] y [[yii\db\Query::each()]] devuelven un objeto [[yii\db\BatchQueryResult]] que |
||||
implementa una interfaz `Iterator` y así se puede usar en el constructor `foreach`. Durante la primera iteración, se |
||||
efectúa una consulta SQL a la base de datos. Desde entonces, los datos se recuperan por lotes en las iteraciones. El |
||||
tamaño predeterminado de los lotes es 100, que significa que se recuperan 100 filas de datos en cada lote. Se puede |
||||
modificar el tamaño de los lotes pasando pasando un primer parámetro a los métodos `batch()` o `each()`. |
||||
|
||||
En comparación con [[yii\db\Query::all()]], las consultas por lotes sólo cargan 100 filas de datos en memoria cada |
||||
vez. Si el procesan los datos y después se descartan inmediatamente, las consultas por lotes, pueden ayudar a mantener |
||||
el uso de memora bajo un limite. |
||||
|
||||
Si se especifica que el resultado de la consulta tiene que ser indexado por alguna columna mediante |
||||
[[yii\db\Query::indexBy()]], las consultas por lotes seguirán manteniendo el indice adecuado. Por ejemplo, |
||||
|
||||
```php |
||||
use yii\db\Query; |
||||
|
||||
$query = (new Query()) |
||||
->from('user') |
||||
->indexBy('username'); |
||||
|
||||
foreach ($query->batch() as $users) { |
||||
// $users esta indexado en la columna "username" |
||||
} |
||||
|
||||
foreach ($query->each() as $username => $user) { |
||||
} |
||||
``` |
@ -0,0 +1,377 @@
|
||||
Clase auxiliar Html (Html helper) |
||||
================================= |
||||
|
||||
Todas las aplicaciones web generan grandes cantidades de marcado HTML (HTML markup). Si el marcado es estático, se |
||||
puede realizar de forma efectiva |
||||
[mezclando PHP y HTML en un mismo archivo](http://php.net/manual/es/language.basic-syntax.phpmode.php) pero cuando se |
||||
generan dinámicamente empieza a complicarse su gestion sin ayuda extra. Yii proporciona esta ayuda que proporciona un |
||||
conjunto de métodos estáticos para gestionar las etiquetas HTML usadas más comúnmente, sus opciones y contenidos. |
||||
|
||||
> Nota: Si el marcado es casi estático, es preferible usar HTML directamente. No es necesario encapsularlo todo con |
||||
llamadas a la clase auxiliar Html. |
||||
|
||||
Lo fundamental <a name="basics"></a> |
||||
-------------- |
||||
|
||||
Teniendo en cuenta que la construcción de HTML dinámico mediante la concatenación de cadenas de texto se complica |
||||
rápidamente, Yii proporciona un conjunto de métodos para manipular las opciones de etiquetas y la construcción de las |
||||
mismas basadas en estas opciones. |
||||
|
||||
### Generación de etiquetas |
||||
|
||||
El código de generación de etiquetas es similar al siguiente: |
||||
|
||||
```php |
||||
<?= Html::tag('p', Html::encode($user->name), ['class' => 'username']) ?> |
||||
``` |
||||
|
||||
El primer argumento es el nombre de la etiqueta. El segundo es el contenido que se ubicara entre la etiqueta de |
||||
apertura y la de cierre. Hay que tener en cuenta que estamos usando `Html::encode`. Esto es debido a que el contenido |
||||
no se codifica automáticamente para permitir usar HTML cuando se necesite. La tercera opción es un array de opciones |
||||
HTML o, en otras palabras, los atributos de las etiquetas. En este array la clave representa el nombre del atributo |
||||
como podría ser `class`, `href` o `target` y el valor es su valor. |
||||
|
||||
El código anterior generara el siguiente HTML: |
||||
|
||||
```html |
||||
<p class="username">samdark</p> |
||||
``` |
||||
|
||||
Si se necesita solo la apertura o el cierre de una etiqueta, se pueden usar los métodos `Html::beginTag()` y |
||||
`Html::endTag()`. |
||||
|
||||
Las opciones se usan en muchos métodos de la clase auxiliar Html y en varios widgets. En todos estos casos hay cierta |
||||
gestión adicional que se debe conocer: |
||||
|
||||
- Si un valor es `null`, el correspondiente atributo no se renderizará. |
||||
- Los atributos cuyos valores son de tipo booleano serán tratados como |
||||
[atributos booleanos](http://www.w3.org/TR/html5/infrastructure.html#boolean-attributes). |
||||
- Los valores de los atributos se codificaran en HTML usando [[yii\helpers\Html::encode()|Html::encode()]]. |
||||
- El atributo "data" puede recibir un array. En este caso, se "expandirá" y se renderizará una lista de atributos |
||||
`data` p. ej. `'data' => ['id' => 1, 'name' => 'yii']` se convierte en `data-id="1" data-name="yii"`. |
||||
- El atributo "data" puede recibir JSON. Se gestionará de la misma manera que un array p. ej. |
||||
`'data' => ['params' => ['id' => 1, 'name' => 'yii'], 'status' => 'ok']` se convierte en |
||||
`data-params='{"id":1,"name":"yii"}' data-status="ok"`. |
||||
|
||||
### Formación de clases y estilos dinámicamente |
||||
|
||||
Cuando se construyen opciones para etiquetas HTML, a menudo nos encontramos con valores predeterminados que hay que |
||||
modificar. Para añadir o eliminar clases CSS se puede usar el siguiente ejemplo: |
||||
|
||||
```php |
||||
$options = ['class' => 'btn btn-default']; |
||||
|
||||
if ($type === 'success') { |
||||
Html::removeCssClass($options, 'btn-default'); |
||||
Html::addCssClass($options, 'btn-success'); |
||||
} |
||||
|
||||
echo Html::tag('div', 'Pwede na', $options); |
||||
|
||||
// cuando $type sea 'success' se renderizará |
||||
// <div class="btn btn-success">Pwede na</div> |
||||
``` |
||||
|
||||
Para hacer lo mismo con los estilos para el atributo `style`: |
||||
|
||||
```php |
||||
$options = ['style' => ['width' => '100px', 'height' => '100px']]; |
||||
|
||||
// devuelve style="width: 100px; height: 200px; position: absolute;" |
||||
Html::addCssStyle($options, 'height: 200px; positon: absolute;'); |
||||
|
||||
// devuelve style="position: absolute;" |
||||
Html::removeCssStyle($options, ['width', 'height']); |
||||
``` |
||||
|
||||
Cuando se usa [[yii\helpers\Html::addCssStyle()|addCssStyle()]] se puede especificar si un array de pares clave-valor |
||||
corresponde a nombres y valores de la propiedad CSS correspondiente o a una cadena de texto como por ejemplo |
||||
`width: 100px; height: 200px;`. Estos formatos se pueden "hacer" y "deshacer" usando |
||||
[[yii\helpers\Html::cssStyleFromArray()|cssStyleFromArray()]] y |
||||
[[yii\helpers\Html::cssStyleToArray()|cssStyleToArray()]]. El método |
||||
[[yii\helpers\Html::removeCssStyle()|removeCssStyle()]] un array de propiedades que se eliminarán. Si sólo se |
||||
eliminará una propiedad, se puede especificar como una cadena de texto. |
||||
|
||||
Codificación y Descodificación de contenido <a name="encoding-and-decoding-content"></a> |
||||
------------------------------------------- |
||||
|
||||
Para que el contenido se muestre correctamente y de forma segura en caracteres especiales HTML el contenido debe ser |
||||
codificado. En PHP esto se hace con [htmlspecialchars](http://www.php.net/manual/en/function.htmlspecialchars.php) y |
||||
[htmlspecialchars_decode](http://www.php.net/manual/en/function.htmlspecialchars-decode.php). El problema con el uso |
||||
de estos métodos directamente es que se tiene que especificar la codificación y opciones extra cada vez. Ya que las |
||||
opciones siempre son las mismas y la codificación debe coincidir con la de la aplicación para prevenir problemas de |
||||
seguridad, Yii proporciona dos métodos simples y compactos: |
||||
|
||||
```php |
||||
$userName = Html::encode($user->name); |
||||
echo $userName; |
||||
|
||||
$decodedUserName = Html::decode($userName); |
||||
``` |
||||
|
||||
Formularios |
||||
----------- |
||||
|
||||
El trato con el marcado de formularios es una tarea repetitiva y propensa a errores. Por esto hay un grupo de métodos |
||||
para ayudar a gestionarlos. |
||||
|
||||
> Nota: hay que considerar la opción de usar [[yii\widgets\ActiveForm|ActiveForm]] en caso que se gestionen |
||||
formularios que requieran validaciones. |
||||
|
||||
### Abrir y cerrar un formulario |
||||
|
||||
Se puede abrir un formulario con el metodo [[yii\helpers\Html::beginForm()|beginForm()]] como se muestra a |
||||
continuación: |
||||
|
||||
```php |
||||
<?= Html::beginForm(['order/update', 'id' => $id], 'post', ['enctype' => 'multipart/form-data']) ?> |
||||
``` |
||||
|
||||
El primer argumento es la URL a la que se enviaran los datos del formulario. Se puede especificar en formato de ruta |
||||
de Yii con los parámetros aceptados por [[yii\helpers\Url::to()|Url::to()]]. El segundo es el método que se usara. |
||||
`post` es el método predeterminado. El tercero es un array de opciones para la etiqueta `form`. En este caso cambiamos |
||||
el método de codificación del formulario de `data` en una petición POST a `multipart/form-data`. Esto se requiere |
||||
cuando se quieren subir archivos. |
||||
|
||||
El cierre de la etiqueta `form` es simple: |
||||
|
||||
```php |
||||
<?= Html::endForm() ?> |
||||
``` |
||||
|
||||
### Botones |
||||
|
||||
Para generar botones se puede usar el siguiente código: |
||||
|
||||
```php |
||||
<?= Html::button('Press me!', ['class' => 'teaser']) ?> |
||||
<?= Html::submitButton('Submit', ['class' => 'submit']) ?> |
||||
<?= Html::resetButton('Reset', ['class' => 'reset']) ?> |
||||
``` |
||||
|
||||
El primer argumento para los tres métodos es el titulo del botón y el segundo son las opciones. El titulo no esta |
||||
codificado pero si se usan datos recibidos por el usuario, deben codificarse mediante |
||||
[[yii\helpers\Html::encode()|Html::encode()]]. |
||||
|
||||
### Inputs |
||||
|
||||
Hay dos grupos en los métodos input. Unos empiezan con `active` y se llaman inputs activos y los otros no empiezan |
||||
así. Los inputs activos obtienen datos del modelo y del atributo especificado y los datos de los inputs normales se |
||||
especifica directamente. |
||||
|
||||
Los métodos más genéricos son: |
||||
|
||||
```php |
||||
type, input name, input value, options |
||||
<?= Html::input('text', 'username', $user->name, ['class' => $username]) ?> |
||||
|
||||
type, model, model attribute name, options |
||||
<?= Html::activeInput('text', $user, 'name', ['class' => $username]) ?> |
||||
``` |
||||
|
||||
Si se conoce el tipo de input de antemano, es conveniente usar los atajos de métodos: |
||||
|
||||
- [[yii\helpers\Html::buttonInput()]] |
||||
- [[yii\helpers\Html::submitInput()]] |
||||
- [[yii\helpers\Html::resetInput()]] |
||||
- [[yii\helpers\Html::textInput()]], [[yii\helpers\Html::activeTextInput()]] |
||||
- [[yii\helpers\Html::hiddenInput()]], [[yii\helpers\Html::activeHiddenInput()]] |
||||
- [[yii\helpers\Html::passwordInput()]] / [[yii\helpers\Html::activePasswordInput()]] |
||||
- [[yii\helpers\Html::fileInput()]], [[yii\helpers\Html::activeFileInput()]] |
||||
- [[yii\helpers\Html::textarea()]], [[yii\helpers\Html::activeTextarea()]] |
||||
|
||||
Los botones de opción (Radios) y las casillas de verificación (checkboxes) se especifican de forma un poco diferente: |
||||
|
||||
```php |
||||
<?= Html::radio('agree', true, ['label' => 'I agree']); |
||||
<?= Html::activeRadio($model, 'agree', ['class' => 'agreement']) |
||||
|
||||
<?= Html::checkbox('agree', true, ['label' => 'I agree']); |
||||
<?= Html::activeCheckbox($model, 'agree', ['class' => 'agreement']) |
||||
``` |
||||
|
||||
Las listas desplegables (dropdown list) se pueden renderizar como se muestra a continuación: |
||||
|
||||
```php |
||||
<?= Html::dropDownList('list', $currentUserId, ArrayHelper::map($userModels, 'id', 'name')) ?> |
||||
<?= Html::activeDropDownList($users, 'id', ArrayHelper::map($userModels, 'id', 'name')) ?> |
||||
|
||||
<?= Html::listBox('list', $currentUserId, ArrayHelper::map($userModels, 'id', 'name')) ?> |
||||
<?= Html::activeListBox($users, 'id', ArrayHelper::map($userModels, 'id', 'name')) ?> |
||||
``` |
||||
|
||||
El primer argumento es el nombre del input, el segundo es el valor seleccionado actualmente y el tercero es el array |
||||
de pares clave-valor donde la clave es al lista de valores y el valor del array es la lista a mostrar. |
||||
|
||||
Si se quiere habilitar la selección múltiple, se puede usar la lista seleccionable (checkbox list): |
||||
|
||||
```php |
||||
<?= Html::checkboxList('roles', [16, 42], ArrayHelper::map($roleModels, 'id', 'name')) ?> |
||||
<?= Html::activeCheckboxList($user, 'role', ArrayHelper::map($roleModels, 'id', 'name')) ?> |
||||
``` |
||||
|
||||
Si no, se puede usar la lista de opciones (radio list): |
||||
|
||||
```php |
||||
<?= Html::radioList('roles', [16, 42], ArrayHelper::map($roleModels, 'id', 'name')) ?> |
||||
<?= Html::activeRadioList($user, 'role', ArrayHelper::map($roleModels, 'id', 'name')) ?> |
||||
``` |
||||
|
||||
### Labels y errores |
||||
|
||||
De forma parecida que en los imputs hay dos métodos para generar labels. El activo que obtiene los datos del modelo y |
||||
el no-activo que acepta datos directamente: |
||||
|
||||
```php |
||||
<?= Html::label('User name', 'username', ['class' => 'label username']) ?> |
||||
<?= Html::activeLabel($user, 'username', ['class' => 'label username']) |
||||
``` |
||||
|
||||
Para mostrar los errores de un modelo o más en forma de resumen: |
||||
|
||||
```php |
||||
<?= Html::errorSummary($posts, ['class' => 'errors']) ?> |
||||
``` |
||||
|
||||
Para mostrar un error individual: |
||||
|
||||
```php |
||||
<?= Html::error($post, 'title', ['class' => 'error']) ?> |
||||
``` |
||||
|
||||
### Nombres y valores |
||||
|
||||
Existen métodos para obtener nombres, IDs y valores para los campos de entrada (inputs) basados en el modelo. Estos se |
||||
usan principalmente internamente pero a veces pueden resultar prácticos: |
||||
|
||||
```php |
||||
// Post[title] |
||||
echo Html::getInputName($post, 'title'); |
||||
|
||||
// post-title |
||||
echo Html::getInputId($post, 'title'); |
||||
|
||||
// mi primer post |
||||
echo Html::getAttributeValue($post, 'title'); |
||||
|
||||
// $post->authors[0] |
||||
echo Html::getAttributeValue($post, '[0]authors[0]'); |
||||
``` |
||||
|
||||
En el ejemplo anterior, el primer argumento es el modelo y el segundo es el atributo de expresión. En su forma más |
||||
simple es su nombre de atributo pero podría ser un nombre de atributo prefijado y/o añadido como sufijo con los |
||||
indices de un array, esto se usa principalmente para mostrar inputs en formatos de tablas: |
||||
|
||||
- `[0]content` se usa en campos de entrada de datos en formato de tablas para representar el atributo "content" para |
||||
el primer modelo del input en formato de tabla; |
||||
- `dates[0]` representa el primer elemento del array del atributo "dates"; |
||||
- `[0]dates[0]` representa el primer elemento del array del atributo "dates" para el primer modelo en formato de tabla. |
||||
|
||||
Para obtener el nombre de atributo sin sufijos o prefijos se puede usar el siguiente código: |
||||
|
||||
```php |
||||
// dates |
||||
echo Html::getAttributeName('dates[0]'); |
||||
``` |
||||
|
||||
Estilos y scripts |
||||
----------------- |
||||
|
||||
Existen dos métodos para generar etiquetas que envuelvan estilos y scripts incrustados (embebbed): |
||||
|
||||
```php |
||||
<?= Html::style('.danger { color: #f00; }') ?> |
||||
|
||||
Genera |
||||
|
||||
<style>.danger { color: #f00; }</style> |
||||
|
||||
<?= Html::script('alert("Hello!");', ['defer' => true]); |
||||
|
||||
Genera |
||||
|
||||
<script defer>alert("Hello!");</script> |
||||
``` |
||||
|
||||
Si se quiere enlazar un estilo externo desde un archivo CSS: |
||||
|
||||
```php |
||||
<?= Html::cssFile('@web/css/ie5.css', ['condition' => 'IE 5']) ?> |
||||
|
||||
genera |
||||
|
||||
<!--[if IE 5]> |
||||
<link href="http://example.com/css/ie5.css" /> |
||||
<![endif]--> |
||||
``` |
||||
|
||||
El primer argumento es la URL. El segundo es un array de opciones. Adicionalmente, para regular las opciones se puede |
||||
especificar: |
||||
|
||||
- `condition` para envolver `<link` con los comentarios condicionales con condiciones especificas. Esperamos que sean |
||||
necesarios los comentarios condicionales ;) |
||||
- `noscript` se puede establecer como `true` para envolver `<link` con la etiqueta `<noscript>` por lo que el sólo se |
||||
incluirá si el navegador no soporta JavaScript o si lo ha deshabilitado el usuario. |
||||
|
||||
Para enlazar un archivo JavaScript: |
||||
|
||||
```php |
||||
<?= Html::jsFile('@web/js/main.js') ?> |
||||
``` |
||||
|
||||
Es igual que con las CSS, el primer argumento especifica el enlace al fichero que se quiere incluir. Las opciones se |
||||
pueden pasar como segundo argumento. En las opciones se puede especificar `condition` del mismo modo que se puede usar |
||||
para `cssFile`. |
||||
|
||||
Enlaces |
||||
------- |
||||
|
||||
Existe un método para generar hipervínculos a conveniencia: |
||||
|
||||
```php |
||||
<?= Html::a('Profile', ['user/view', 'id' => $id], ['class' => 'profile-link']) ?> |
||||
``` |
||||
|
||||
El primer argumento es el titulo. No está codificado por lo que si se usan datos enviados por el usuario se tienen que |
||||
codificar usando `Html::encode()`. El segundo argumento es el que se introducirá en `href` de la etiqueta `<a`. Se |
||||
puede revisar [Url::to()](helper-url.md) para obtener más detalles de los valores que acepta. El tercer argumento es |
||||
un array de las propiedades de la etiqueta. |
||||
|
||||
Si se requiere generar enlaces de tipo `mailto` se puede usar el siguiente código: |
||||
|
||||
```php |
||||
<?= Html::mailto('Contact us', 'admin@example.com') ?> |
||||
``` |
||||
|
||||
Imagenes |
||||
-------- |
||||
|
||||
Para generar una etiqueta de tipo imagen se puede usar el siguiente ejemplo: |
||||
|
||||
```php |
||||
<?= Html::img('@web/images/logo.png', ['alt' => 'My logo']) ?> |
||||
|
||||
genera |
||||
|
||||
<img src="http://example.com/images/logo.png" alt="My logo" /> |
||||
``` |
||||
|
||||
Aparte de los [aliases](concept-aliases.md) el primer argumento puede aceptar rutas, parámetros y URLs. Del mismo modo |
||||
que [Url::to()](helper-url.md). |
||||
|
||||
Listas |
||||
------ |
||||
|
||||
Las listas desordenadas se puede generar como se muestra a continuación: |
||||
|
||||
```php |
||||
<?= Html::ul($posts, ['item' => function($item, $index) { |
||||
return Html::tag( |
||||
'li', |
||||
$this->render('post', ['item' => $item]), |
||||
['class' => 'post'] |
||||
); |
||||
}]) ?> |
||||
``` |
||||
|
||||
Para generar listas ordenadas se puede usar `Html::ol()` en su lugar. |
@ -0,0 +1,156 @@
|
||||
Clase Auxiliar URL (URL Helper) |
||||
=============================== |
||||
|
||||
La clase auxiliar URL proporciona un conjunto de métodos estáticos para gestionar URLs. |
||||
|
||||
Obtener URLs Comunes |
||||
-------------------- |
||||
|
||||
Se pueden usar dos métodos para obtener URLs comunes: URL de inicio (home URL) y URL base (base URL) de la petición |
||||
(request) actual. Para obtener la URL de inicio se puede usar el siguiente código: |
||||
|
||||
```php |
||||
$relativeHomeUrl = Url::home(); |
||||
$absoluteHomeUrl = Url::home(true); |
||||
$httpsAbsoluteHomeUrl = Url::home('https'); |
||||
``` |
||||
|
||||
Si no se pasan parámetros, las URLs generadas son relativas. Se puede pasar `true`para obtener la URL absoluta del |
||||
esquema actual o especificar el esquema explícitamente (`https`, `http`). |
||||
|
||||
Para obtener la URL base de la petición actual, se puede usar el siguiente código: |
||||
|
||||
```php |
||||
$relativeBaseUrl = Url::base(); |
||||
$absoluteBaseUrl = Url::base(true); |
||||
$httpsAbsoluteBaseUrl = Url::base('https'); |
||||
``` |
||||
|
||||
El único parámetro del método funciona exactamente igual que para `Url::home()`. |
||||
|
||||
Creación de URLs |
||||
---------------- |
||||
|
||||
Para crear una URL para una ruta determinada se puede usar `Url::toRoute()`. El metodo utiliza [[\yii\web\UrlManager]] |
||||
para crear una URL: |
||||
|
||||
```php |
||||
$url = Url::toRoute(['product/view', 'id' => 42]); |
||||
``` |
||||
|
||||
Se puede especificar la ruta como una cadena de texto, p. ej. `site/index`. También se puede usar un array si se |
||||
quieren especificar parámetros para la URL que se esta generando. El formato del array debe ser: |
||||
|
||||
```php |
||||
// genera: /index.php?r=site/index¶m1=value1¶m2=value2 |
||||
['site/index', 'param1' => 'value1', 'param2' => 'value2'] |
||||
``` |
||||
|
||||
Si se quiere crear una URL con un enlace, se puede usar el formato de array con el parámetro `#`. Por ejemplo, |
||||
|
||||
```php |
||||
// genera: /index.php?r=site/index¶m1=value1#name |
||||
['site/index', 'param1' => 'value1', '#' => 'name'] |
||||
``` |
||||
|
||||
Una ruta puede ser absoluta o relativa. Una ruta absoluta tiene una barra al principio (p. ej. `/site/index`), |
||||
mientras que una ruta relativa no la tiene (p. ej. `site/index` o `index`). Una ruta relativa se convertirá en una |
||||
ruta absoluta siguiendo las siguientes normas: |
||||
|
||||
- Si la ruta es una cadena vacía, se usará la [[\yii\web\Controller::route|route]] actual; |
||||
- Si la ruta no contiene barras (p. ej. `index`), se considerará que es el ID de una acción del controlador actual y |
||||
se antepondrá con [[\yii\web\Controller::uniqueId]]; |
||||
- Si la ruta no tiene barra inicial (p. ej. `site/index`), se considerará que es una ruta relativa del modulo actual y |
||||
se le antepondrá el [[\yii\base\Module::uniqueId|uniqueId]] del modulo. |
||||
|
||||
A continuación se muestran varios ejemplos del uso de este método: |
||||
|
||||
```php |
||||
// /index?r=site/index |
||||
echo Url::toRoute('site/index'); |
||||
|
||||
// /index?r=site/index&src=ref1#name |
||||
echo Url::toRoute(['site/index', 'src' => 'ref1', '#' => 'name']); |
||||
|
||||
// http://www.example.com/index.php?r=site/index |
||||
echo Url::toRoute('site/index', true); |
||||
|
||||
// https://www.example.com/index.php?r=site/index |
||||
echo Url::toRoute('site/index', 'https'); |
||||
``` |
||||
|
||||
El otro método `Url::to()` es muy similar a [[toRoute()]]. La única diferencia es que este método requiere que la ruta |
||||
especificada sea un array. Si se pasa una cadena de texto, se tratara como una URL. |
||||
|
||||
El primer argumento puede ser: |
||||
|
||||
- un array: se llamará a [[toRoute()]] para generar la URL. Por ejemplo: `['site/index']`, |
||||
`['post/index', 'page' => 2]`. Se puede revisar [[toRoute()]] para obtener más detalles acerca de como especificar |
||||
una ruta. |
||||
- una cadena que empiece por `@`: se tratará como un alias, y se devolverá la cadena correspondiente asociada a este |
||||
alias. |
||||
- una cadena vacía: se devolverá la URL de la petición actual; |
||||
- una cadena de texto: se devolverá sin alteraciones. |
||||
|
||||
Cuando se especifique `$schema` (tanto una cadena de text como `true`), se devolverá una URL con información del host |
||||
(obtenida mediante [[\yii\web\UrlManager::hostInfo]]). Si `$url` ya es una URL absoluta, su esquema se reemplazará con |
||||
el especificado. |
||||
|
||||
A continuación se muestran algunos ejemplos de uso: |
||||
|
||||
```php |
||||
// /index?r=site/index |
||||
echo Url::to(['site/index']); |
||||
|
||||
// /index?r=site/index&src=ref1#name |
||||
echo Url::to(['site/index', 'src' => 'ref1', '#' => 'name']); |
||||
|
||||
// la URL solicitada actualmente |
||||
echo Url::to(); |
||||
|
||||
// /images/logo.gif |
||||
echo Url::to('@web/images/logo.gif'); |
||||
|
||||
// images/logo.gif |
||||
echo Url::to('images/logo.gif'); |
||||
|
||||
// http://www.example.com/images/logo.gif |
||||
echo Url::to('@web/images/logo.gif', true); |
||||
|
||||
// https://www.example.com/images/logo.gif |
||||
echo Url::to('@web/images/logo.gif', 'https'); |
||||
``` |
||||
|
||||
Recordar la URL para utilizarla más adelante |
||||
-------------------------------------------- |
||||
|
||||
Hay casos en que se necesita recordar la URL y después usarla durante el procesamiento de una de las peticiones |
||||
secuenciales. Se puede logar de la siguiente manera: |
||||
|
||||
```php |
||||
// Recuerda la URL actual |
||||
Url::remember(); |
||||
|
||||
// Recuerda la URL especificada. Revisar Url::to() para ver formatos de argumentos. |
||||
Url::remember(['product/view', 'id' => 42]); |
||||
|
||||
// Recuerda la URL especificada con un nombre asignado |
||||
Url::remember(['product/view', 'id' => 42], 'product'); |
||||
``` |
||||
|
||||
En la siguiente petición se puede obtener la URL memorizada de la siguiente manera: |
||||
|
||||
```php |
||||
$url = Url::previous(); |
||||
$productUrl = Url::previous('product'); |
||||
``` |
||||
|
||||
Reconocer la relatividad de URLs |
||||
-------------------------------- |
||||
|
||||
Para descubrir si una URL es relativa, es decir, que no contenga información del host, se puede utilizar el siguiente |
||||
código: |
||||
|
||||
```php |
||||
$isRelative = Url::isRelative('test/it'); |
||||
``` |
@ -0,0 +1,99 @@
|
||||
Temas |
||||
===== |
||||
|
||||
> Nota: Esta sección está en desarrollo. |
||||
|
||||
Un tema (theme) es un directorio de archivos y de vistas (views) y layouts. Cada archivo de este directorio |
||||
sobrescribe el archivo correspondiente de una aplicación cuando se renderiza. Una única aplicación puede usar |
||||
múltiples temas para que pueden proporcionar experiencias totalmente diferentes. Solo se puede haber un único tema |
||||
activo. |
||||
|
||||
> Nota: Los temas no están destinados a ser redistribuidos ya que están demasiado ligados a la aplicación. Si se |
||||
quiere redistribuir una apariencia personalizada, se puede considerar la opción de |
||||
[asset bundles](structure-assets.md) de archivos CSS y Javascript. |
||||
|
||||
Configuración de un Tema |
||||
------------------------ |
||||
|
||||
La configuración de un tema se especifica a través del componente `view` de la aplicación. Para establecer que un tema |
||||
trabaje con vistas de aplicación básicas, la configuración de la aplicación debe contener lo siguiente: |
||||
|
||||
```php |
||||
'components' => [ |
||||
'view' => [ |
||||
'theme' => [ |
||||
'pathMap' => ['@app/views' => '@app/themes/basic'], |
||||
'baseUrl' => '@web/themes/basic', |
||||
], |
||||
], |
||||
], |
||||
``` |
||||
|
||||
En el ejemplo anterior, el `pathMap` define un mapa (map) de las rutas a las que se aplicará el tema mientras que |
||||
`baseUrl` define la URL base para los recursos a los que hacen referencia los archivos del tema. |
||||
|
||||
En nuestro caso `pathMap` es `['@app/views' => '@app/themes/basic']`. Esto significa que cada vista de `@app/views` |
||||
primero se buscará en `@app/themes/basic` y si existe, se usará la vista del directorio del tema en lugar de la vista |
||||
original. |
||||
|
||||
Por ejemplo, con la configuración anterior, la versión del tema para la vista `@app/views/site/index.php` será |
||||
`@app/themes/basic/site/index.php`. Básicamente se reemplaza `@app/views` en `@app/views/site/index.php` por |
||||
`@app/themes/basic`. |
||||
|
||||
### Temas para Módulos |
||||
|
||||
Para utilizar temas en los módulos, el `pathMap` debe ser similar al siguiente: |
||||
|
||||
```php |
||||
'components' => [ |
||||
'view' => [ |
||||
'theme' => [ |
||||
'pathMap' => [ |
||||
'@app/views' => '@app/themes/basic', |
||||
'@app/modules' => '@app/themes/basic/modules', // <-- !!! |
||||
], |
||||
], |
||||
], |
||||
], |
||||
``` |
||||
|
||||
Esto permite aplicar el tema a `@app/modules/blog/views/comment/index.php` con la vista |
||||
`@app/themes/basic/modules/blog/views/comment/index.php`. |
||||
|
||||
### Temas para Widgets |
||||
|
||||
Para utilizar un tema en una vista que se encuentre en `@app/widgets/currency/views/index.php`, se debe aplicar la |
||||
siguiente configuración para el componente vista, tema: |
||||
|
||||
```php |
||||
'components' => [ |
||||
'view' => [ |
||||
'theme' => [ |
||||
'pathMap' => ['@app/widgets' => '@app/themes/basic/widgets'], |
||||
], |
||||
], |
||||
], |
||||
``` |
||||
|
||||
Con la configuración anterior, se puede crear una versión de la vista `@app/widgets/currency/index.php` para que se |
||||
aplique el tema en `@app/themes/basic/widgets/currency/index.php`. |
||||
|
||||
Uso de Multiples Rutas |
||||
---------------------- |
||||
|
||||
Es posible mapear una única ruta a múltiples rutas de temas. Por ejemplo: |
||||
|
||||
```php |
||||
'pathMap' => [ |
||||
'@app/views' => [ |
||||
'@app/themes/christmas', |
||||
'@app/themes/basic', |
||||
], |
||||
] |
||||
``` |
||||
|
||||
En este caso, primero se buscara la vista en `@app/themes/christmas/site/index.php`, si no se encuentra, se intentará |
||||
en `@app/themes/basic/site/index.php`. Si la vista no se encuentra en ninguna de rutas especificadas, se usará la |
||||
vista de aplicación. |
||||
|
||||
Esta capacidad es especialmente útil si se quieren sobrescribir algunas rutas temporal o condicionalmente. |
@ -0,0 +1,88 @@
|
||||
クラスのオートローディング |
||||
================= |
||||
|
||||
Yiiは、必要となるすべてのクラスファイルを、特定してインクルードするにあたり、 [クラスのオートローディングメカニズム](http://www.php.net/manual/en/language.oop5.autoload.php) |
||||
を頼りにします。[PSR-4 標準](https://github.com/php-fig/fig-standards/blob/master/proposed/psr-4-autoloader/psr-4-autoloader.md) に準拠した、高性能なクラスのオートローダーを提供します。 |
||||
このオートローダーは、あなたが `Yii.php` ファイルをインクルードするときにインストールされます。 |
||||
|
||||
> 補足: 説明を簡単にするため、このセクションではクラスのオートローディングについてのみお話しします。しかし、 |
||||
ここに記述されている内容は、同様に、インタフェースとトレイトのオートロードにも適用されることに注意してください。 |
||||
|
||||
|
||||
Yii オートローダーの使用 <a name="using-yii-autoloader"></a> |
||||
------------------------ |
||||
|
||||
Yii のクラスオートローダーを使用するには、自分のクラスを作成して名前を付けるとき、次の2つの単純なルールに従わなければなりません: |
||||
|
||||
* 各クラスは名前空間の下になければなりません (例 `foo\bar\MyClass`) |
||||
* 各クラスは次のアルゴリズムで決定される個別のファイルに保存されなければなりません: |
||||
|
||||
```php |
||||
// $className は先頭にバックスラッシュを持つ完全修飾名 |
||||
$classFile = Yii::getAlias('@' . str_replace('\\', '/', $className) . '.php'); |
||||
``` |
||||
たとえば、クラス名と名前空間が `foo\bar\MyClass` であれば、対応するクラスファイルのパスの [エイリアス](concept-aliases.md) は、 |
||||
`@foo/bar/MyClass.php` になります。このエイリアスがファイルパスになるようにするには、`@foo` または `@foo/bar` |
||||
のどちらかが、 [ルートエイリアス](concept-aliases.md#defining-aliases) でなければなりません。 |
||||
|
||||
[Basic Application Template](start-basic.md) を使用している場合、最上位の名前空間 `app` の下にクラスを置くことができ、 |
||||
そうすると、新しいエイリアスを定義しなくても、Yii によってそれらをオートロードできるようになります。これは `@app` |
||||
が [事前定義されたエイリアス](concept-aliases.md#predefined-aliases) であるためで、`app\components\MyClass` のようなクラス名を |
||||
今説明したアルゴリズムに従って、クラスファイル `AppBasePath/components/MyClass.php` だと解決できるのです。 |
||||
|
||||
[Advanced Application Template](tutorial-advanced-app.md) では、各階層にそれ自身のルートエイリアスを持っています。たとえば、 |
||||
フロントエンド層はルートエイリアス `@frontend` を持ち、バックエンド層は `@backend` です。その結果、名前空間 `frontend` の下に |
||||
フロントエンドクラスを置き、バックエンドクラスを `backend` の下に置けます。これで、これらのクラスは Yii のオートローダーによって |
||||
オートロードできるようになります。 |
||||
|
||||
|
||||
クラスマップ <a name="class-map"></a> |
||||
--------- |
||||
|
||||
Yii のクラスオートローダーは、 *クラスマップ* 機能をサポートしており、クラス名を対応するクラスファイルのパスにマップできます。 |
||||
オートローダーがクラスをロードしているとき、クラスがマップに見つかるかどうかを最初にチェックします。もしあれば、対応する |
||||
ファイルのパスは、それ以上チェックされることなく、直接インクルードされます。これでクラスのオートローディングを非常に高速化できます。 |
||||
実際に、すべての Yii のコアクラスは、この方法でオートロードされています。 |
||||
|
||||
次の方法で、 `Yii::$classMap` に格納されるクラスマップにクラスを追加できます: |
||||
|
||||
```php |
||||
Yii::$classMap['foo\bar\MyClass'] = 'path/to/MyClass.php'; |
||||
``` |
||||
|
||||
クラスファイルのパスを指定するのに、 [エイリアス](concept-aliases.md) を使うことができます。クラスが使用される前にマップが準備できるように、 |
||||
[ブートストラップ](runtime-bootstrapping.md) プロセス内でクラスマップを設定する必要があります。 |
||||
|
||||
|
||||
他のオートローダーの使用 <a name="using-other-autoloaders"></a> |
||||
----------------------- |
||||
|
||||
Yii はパッケージ依存関係マネージャとして Composer を包含しているので、Composer のオートローダーもインストールすることをお勧めします。 |
||||
あなたが独自のオートローダーを持つサードパーティライブラリを使用している場合、それらもインストールする必要があります。 |
||||
|
||||
Yii オートローダーを他のオートローダーと一緒に使うときは、他のすべてのオートローダーがインストールされた *後で* 、 `Yii.php` |
||||
ファイルをインクルードする必要があります。これで Yii のオートローダーが、任意クラスのオートローディング要求に応答する最初のものになります。 |
||||
たとえば、次のコードは [Basic Application Template](start-basic.md) の [エントリスクリプト](structure-entry-scripts.md) から抜粋したものです。 |
||||
最初の行は、Composer のオートローダーをインストールしており、二行目は Yii のオートローダーをインストールしています。 |
||||
|
||||
```php |
||||
require(__DIR__ . '/../vendor/autoload.php'); |
||||
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php'); |
||||
``` |
||||
|
||||
あなたは Yii のオートローダーを使わず、Composer のオートローダーだけを単独で使用することもできます。しかし、そうすることによって、 |
||||
あなたのクラスのオートローディングのパフォーマンスは低下し、クラスをオートロード可能にするために Composer が設定したルールに従わなければならなくなります。 |
||||
|
||||
> Info: Yiiのオートローダーを使用したくない場合は、 `Yii.php` ファイルの独自のバージョンを作成し、 |
||||
それを [エントリスクリプト](structure-entry-scripts.md) でインクルードする必要があります。 |
||||
|
||||
|
||||
エクステンションクラスのオートロード <a name="autoloading-extension-classes"></a> |
||||
----------------------------- |
||||
|
||||
Yii のオートローダーは、 [エクステンション](structure-extensions.md) クラスのオートロードが可能です。唯一の要件は、 |
||||
エクステンションがその `composer.json` ファイルに正しく `autoload` セクションを指定していることです。 |
||||
`autoload` 指定方法の詳細については [Composer のドキュメント](https://getcomposer.org/doc/04-schema.md#autoload) 参照してください。 |
||||
|
||||
Yii のオートローダーを使用しない場合でも、まだ Composer のオートローダーはエクステンションクラスをオートロード可能です。 |
||||
|
@ -0,0 +1,90 @@
|
||||
コンポーネント |
||||
========== |
||||
|
||||
コンポーネントは、Yiiアプリケーションの主要な構成ブロックです。コンポーネントは [[yii\base\Component]] 、 |
||||
またはその派生クラスのインスタンスです。コンポーネントが他のクラスに提供する主な機能は次の 3 つです: |
||||
|
||||
* [プロパティ](concept-properties.md) |
||||
* [イベント](concept-events.md) |
||||
* [ビヘイビア](concept-behaviors.md) |
||||
|
||||
個々にでも、組み合わせでも、これらの機能は Yii のクラスのカスタマイズ性と使いやすさをとても高めてくれます。たとえば、[[yii\jui\DatePicker|日付選択]] を行うユーザインターフェース·コンポーネントは、 |
||||
対話型の日付選択UIを生成するとき、ビューで次のように使用することができます: |
||||
|
||||
```php |
||||
use yii\jui\DatePicker; |
||||
|
||||
echo DatePicker::widget([ |
||||
'language' => 'ja', |
||||
'name' => 'country', |
||||
'clientOptions' => [ |
||||
'dateFormat' => 'yy-mm-dd', |
||||
], |
||||
]); |
||||
``` |
||||
|
||||
クラスが [[yii\base\Component]] を継承しているおかげで、ウィジェットのプロパティは簡単に記述できます。 |
||||
|
||||
コンポーネントは非常に強力ですが、 [イベント](concept-events.md) と [ビヘイビア](concept-behaviors.md) をサポートするため、 |
||||
余分にメモリとCPU時間を要し、通常のオブジェクトよりも少し重くなります。 |
||||
あなたのコンポーネントがこれら2つの機能を必要としない場合、[[yii\base\Component]] の代わりに、 [[yii\base\Object]] からコンポーネントクラスを派生することを検討してもよいでしょう。 |
||||
そうすることで、あなたのコンポーネントは、 [プロパティ](concept-properties.md) のサポートが維持されたまま、通常のPHPオブジェクトのように効率的になります。 |
||||
|
||||
[[yii\base\Component]] または [[yii\base\Object]] からクラスを派生するときは、次の規約に従うことが推奨されます: |
||||
|
||||
- コンストラクタをオーバーライドする場合は、コンストラクタの *最後の* パラメータとして `$config` パラメータを指定し、親のコンストラクタにこのパラメータを渡すこと。 |
||||
- 自分がオーバーライドしたコンストラクタの *最後で* 、必ず親クラスのコンストラクタを呼び出すこと。 |
||||
- [[yii\base\Object::init()]] メソッドをオーバーライドする場合は、自分の `init` メソッドの *最初に* 、必ず `init` の親実装を呼び出すようにすること。 |
||||
|
||||
例: |
||||
|
||||
```php |
||||
namespace yii\components\MyClass; |
||||
|
||||
use yii\base\Object; |
||||
|
||||
class MyClass extends Object |
||||
{ |
||||
public $prop1; |
||||
public $prop2; |
||||
|
||||
public function __construct($param1, $param2, $config = []) |
||||
{ |
||||
// ... 構成前の初期化 |
||||
|
||||
parent::__construct($config); |
||||
} |
||||
|
||||
public function init() |
||||
{ |
||||
parent::init(); |
||||
|
||||
// ... 構成後の初期化 |
||||
} |
||||
} |
||||
``` |
||||
このガイドラインに従うことで、あなたのコンポーネントは生成時に [コンフィグ可能](concept-configurations.md) になります。例: |
||||
|
||||
```php |
||||
$component = new MyClass(1, 2, ['prop1' => 3, 'prop2' => 4]); |
||||
// とする代わりに |
||||
$component = \Yii::createObject([ |
||||
'class' => MyClass::className(), |
||||
'prop1' => 3, |
||||
'prop2' => 4, |
||||
], [1, 2]); |
||||
``` |
||||
|
||||
> 補足: [[Yii::createObject()]] を呼び出すアプローチは複雑に見えますが、より強力です。というのも、それが [依存性注入コンテナ](concept-di-container.md) 上に実装されているからです。 |
||||
|
||||
|
||||
[[yii\base\Object]] クラスには、次のオブジェクトライフサイクルが適用されます: |
||||
|
||||
1. コンストラクタ内の事前初期化。ここでデフォルトのプロパティ値を設定することができます。 |
||||
2. `$config` によるオブジェクトの構成。構成情報は、コンストラクタ内で設定されたデフォルト値を上書きすることがあります。 |
||||
3. [[yii\base\Object::init()|init()]] 内の事後初期化。サニティ・チェックやプロパティの正規化を行いたいときは、このメソッドをオーバーライドします。 |
||||
4. オブジェクトのメソッド呼び出し。 |
||||
|
||||
最初の 3 つのステップは、すべてのオブジェクトのコンストラクタ内で発生します。これは、あなたがクラスインスタンス (つまり、オブジェクト) を得たときには、 |
||||
すでにそのオブジェクトが適切な、信頼性の高い状態に初期化されていることを意味します。 |
||||
|
@ -0,0 +1,260 @@
|
||||
構成情報 |
||||
============== |
||||
|
||||
新しいオブジェクトを作成したり、既存のオブジェクトを初期化するとき、Yiiでは構成情報が広く使用されています。構成情報は通常、作成されるオブジェクトのクラス名、およびオブジェクトの [プロパティ](concept-properties.md) |
||||
に割り当てられる初期値のリストを含みます。構成情報は、オブジェクトの [イベント](concept-events.md) にアタッチされるハンドラのリストや、オブジェクトにアタッチされる |
||||
[ビヘイビア](concept-behaviors.md) のリストを含むこともできます。 |
||||
|
||||
以下では、データベース接続を作成して初期化するために、構成情報が使用されています: |
||||
|
||||
```php |
||||
$config = [ |
||||
'class' => 'yii\db\Connection', |
||||
'dsn' => 'mysql:host=127.0.0.1;dbname=demo', |
||||
'username' => 'root', |
||||
'password' => '', |
||||
'charset' => 'utf8', |
||||
]; |
||||
|
||||
$db = Yii::createObject($config); |
||||
``` |
||||
[[Yiiの::CreateObject()]] メソッドは引数に構成情報の配列を受け取り、構成情報で名前指定されたクラスをインスタンス化してオブジェクトを作成します。オブジェクトがインスタンス化されるとき、その他の設定は、 |
||||
オブジェクトのプロパティ、イベントハンドラ、およびビヘイビアを初期化するのに使われます。 |
||||
|
||||
すでにオブジェクトがある場合は、構成情報配列でオブジェクトのプロパティを初期化するのに [[Yii::configure()]] を使用することができます: |
||||
|
||||
```php |
||||
Yii::configure($object, $config); |
||||
``` |
||||
|
||||
なお、この場合には、構成情報配列に `class` 要素を含んではいけません。 |
||||
|
||||
|
||||
## 構成情報の形式 <a name="configuration-format"></a> |
||||
|
||||
構成情報の形式は、フォーマルには次のように説明できます: |
||||
|
||||
```php |
||||
[ |
||||
'class' => 'ClassName', |
||||
'propertyName' => 'propertyValue', |
||||
'on eventName' => $eventHandler, |
||||
'as behaviorName' => $behaviorConfig, |
||||
] |
||||
``` |
||||
|
||||
ここで |
||||
|
||||
* `class` 要素は、作成されるオブジェクトの完全修飾クラス名を指定します。 |
||||
* `propertyName` 要素は、名前で指定されたプロパティの初期値を指定します。キーはプロパティ名で、値はそれに対応する初期値です。 |
||||
パブリックメンバ変数と getter/setter によって定義されている [プロパティ](concept-properties.md) のみを設定することができます。 |
||||
* `on eventName` 要素は、どのようなハンドラがオブジェクトの [イベント](concept-events.md) にアタッチされるかを指定します。 |
||||
配列のキーが `on` に続けてイベント名という書式になることに注意してください。サポートされているイベントハンドラの形式については、 |
||||
[イベント](concept-events.md) のセクションを参照してください。 |
||||
* `as behaviorName` 要素は、どのような [ビヘイビア](concept-behaviors.md) がオブジェクトにアタッチされるかを指定します。 |
||||
配列のキーが `as` に続けてビヘイビア名という書式になり、 `$behaviorConfig` で示される値が、ここで説明する一般的な構成情報のような、 |
||||
ビヘイビアを作成するための構成情報になることに注意してください。 |
||||
|
||||
下記は、初期プロパティ値、イベントハンドラ、およびビヘイビアでの構成を示した例です: |
||||
|
||||
```php |
||||
[ |
||||
'class' => 'app\components\SearchEngine', |
||||
'apiKey' => 'xxxxxxxx', |
||||
'on search' => function ($event) { |
||||
Yii::info("Keyword searched: " . $event->keyword); |
||||
}, |
||||
'as indexer' => [ |
||||
'class' => 'app\components\IndexerBehavior', |
||||
// ... プロパティ初期値 ... |
||||
], |
||||
] |
||||
``` |
||||
|
||||
|
||||
## 構成情報の使用 <a name="using-configurations"></a> |
||||
|
||||
構成情報は Yii の多くの場所で使用されています。このセクションの冒頭では、 [[Yii::createObject()]] |
||||
を使って、構成情報に応じてオブジェクトを作成する方法を示しました。このサブセクションでは、 |
||||
アプリケーションの構成とウィジェットの構成という、2つの主要な構成情報の用途を説明します。 |
||||
|
||||
|
||||
### アプリケーションの構成 <a name="application-configurations"></a> |
||||
|
||||
[アプリケーション](structure-applications.md) の構成は、おそらく Yii の中で最も複雑な配列のひとつです。 |
||||
それは [[yii\web\Application|application]] クラスが、設定可能なプロパティとイベントを数多く持つためです。 |
||||
さらに重要なことは、その [[yii\web\Application::components|components]] プロパティが、アプリケーションに登録されている |
||||
コンポーネント生成用の構成情報配列を受け取ることができることです。以下は、 [basic application template](start-basic.md) |
||||
のアプリケーション構成ファイルの概要です。 |
||||
|
||||
```php |
||||
$config = [ |
||||
'id' => 'basic', |
||||
'basePath' => dirname(__DIR__), |
||||
'extensions' => require(__DIR__ . '/../vendor/yiisoft/extensions.php'), |
||||
'components' => [ |
||||
'cache' => [ |
||||
'class' => 'yii\caching\FileCache', |
||||
], |
||||
'mailer' => [ |
||||
'class' => 'yii\swiftmailer\Mailer', |
||||
], |
||||
'log' => [ |
||||
'class' => 'yii\log\Dispatcher', |
||||
'traceLevel' => YII_DEBUG ? 3 : 0, |
||||
'targets' => [ |
||||
[ |
||||
'class' => 'yii\log\FileTarget', |
||||
], |
||||
], |
||||
], |
||||
'db' => [ |
||||
'class' => 'yii\db\Connection', |
||||
'dsn' => 'mysql:host=localhost;dbname=stay2', |
||||
'username' => 'root', |
||||
'password' => '', |
||||
'charset' => 'utf8', |
||||
], |
||||
], |
||||
]; |
||||
``` |
||||
|
||||
この構成情報には、 `class` キーがありません。それは、[エントリスクリプト](structure-entry-scripts.md) で以下のように、 |
||||
クラス名が既に与えられて使用されているためです。 |
||||
|
||||
```php |
||||
(new yii\web\Application($config))->run(); |
||||
``` |
||||
|
||||
アプリケーションの `components` プロパティ構成の詳細については、 [アプリケーション](structure-applications.md) セクションと [サービスロケータ](concept-service-locator.md) セクションにあります。 |
||||
|
||||
|
||||
### ウィジェットの構成 <a name="widget-configurations"></a> |
||||
|
||||
[ウィジェット](structure-widgets.md) を使用するときは、多くの場合、ウィジェットのプロパティをカスタマイズするために、構成情報を使用する必要があります。 |
||||
[[yii\base\Widget::widget()]] と [[yii\base\Widget::begin()]] の両メソッドを使って、ウィジェットを作成できます。それらは、以下のような構成情報配列を取ります。 |
||||
|
||||
```php |
||||
use yii\widgets\Menu; |
||||
|
||||
echo Menu::widget([ |
||||
'activateItems' => false, |
||||
'items' => [ |
||||
['label' => 'ホーム', 'url' => ['site/index']], |
||||
['label' => '製品', 'url' => ['product/index']], |
||||
['label' => 'ログイン', 'url' => ['site/login'], 'visible' => Yii::$app->user->isGuest], |
||||
], |
||||
]); |
||||
``` |
||||
|
||||
上記のコードは、 `Menu` ウィジェットを作成し、その `activateItems` プロパティが `false` になるよう初期化します。 |
||||
`items` プロパティも、表示されるメニュー項目で構成されます。 |
||||
|
||||
クラス名がすでに与えられているので、構成情報配列が `class` キーを持つべきではないことに注意してください。 |
||||
|
||||
|
||||
## 構成情報ファイル <a name="configuration-files"></a> |
||||
|
||||
構成情報がとても複雑になる場合、一般的な方法は、 *構成情報ファイル* と呼ばれる、ひとつまたは複数の PHP ファイルにそれを格納することです。 |
||||
構成情報ファイルは、構成情報を表す PHP 配列を return します。 |
||||
たとえば、次のように、 `web.php` と名づけたファイルにアプリケーション構成を保持することができます。 |
||||
|
||||
```php |
||||
return [ |
||||
'id' => 'basic', |
||||
'basePath' => dirname(__DIR__), |
||||
'extensions' => require(__DIR__ . '/../vendor/yiisoft/extensions.php'), |
||||
'components' => require(__DIR__ . '/components.php'), |
||||
]; |
||||
``` |
||||
`components` の構成もまた複雑になるため、上記のように、 `components.php` と呼ぶ別のファイルにそれを格納し `web.php` でそのファイルを "require" しています。 |
||||
この `components.php` の内容は、次のようになっています。 |
||||
|
||||
```php |
||||
return [ |
||||
'cache' => [ |
||||
'class' => 'yii\caching\FileCache', |
||||
], |
||||
'mailer' => [ |
||||
'class' => 'yii\swiftmailer\Mailer', |
||||
], |
||||
'log' => [ |
||||
'class' => 'yii\log\Dispatcher', |
||||
'traceLevel' => YII_DEBUG ? 3 : 0, |
||||
'targets' => [ |
||||
[ |
||||
'class' => 'yii\log\FileTarget', |
||||
], |
||||
], |
||||
], |
||||
'db' => [ |
||||
'class' => 'yii\db\Connection', |
||||
'dsn' => 'mysql:host=localhost;dbname=stay2', |
||||
'username' => 'root', |
||||
'password' => '', |
||||
'charset' => 'utf8', |
||||
], |
||||
]; |
||||
``` |
||||
|
||||
構成情報ファイルに格納されている構成情報を取得するには、以下のように、それを "require" するだけです: |
||||
|
||||
```php |
||||
$config = require('path/to/web.php'); |
||||
(new yii\web\Application($config))->run(); |
||||
``` |
||||
|
||||
|
||||
## デフォルト設定 <a name="default-configurations"></a> |
||||
|
||||
[[Yii::createObject()]] メソッドは、 [依存性注入コンテナ](concept-di-container.md) をベースに実装されています。 |
||||
そのため、指定されたクラスが [[Yii::createObject()]] を使用して作成されるとき、そのすべてのインスタンスに適用される、 |
||||
いわゆる *デフォルト設定* のセットを指定することができます。デフォルト設定は、 |
||||
[ブートストラップ](runtime-bootstrapping.md) コード内の `Yii::$container->set()` を呼び出すことで指定することができます。 |
||||
|
||||
たとえばあなたが、すべてのリンクページャーが最大で5つのページボタン (デフォルト値は10) を伴って表示されるよう |
||||
[[yii\widgets\LinkPager]] をカスタマイズしたいとき、その目標を達成するには次のコードを使用することができます。 |
||||
|
||||
```php |
||||
\Yii::$container->set('yii\widgets\LinkPager', [ |
||||
'maxButtonCount' => 5, |
||||
]); |
||||
``` |
||||
|
||||
デフォルト設定を使用しなければ、あなたは、リンクページャーを使うすべての箇所で `maxButtonCount` を設定しなければなりません。 |
||||
|
||||
|
||||
## 環境定数 <a name="environment-constants"></a> |
||||
|
||||
構成情報は、多くの場合、アプリケーションが実行される環境に応じて変化します。たとえば、 |
||||
開発環境では `mydb_dev` という名前のデータベースを使用し、本番サーバー上では `mydb_prod` データベースを |
||||
使用したいかもしれません。環境の切り替えを容易にするために、Yii は、あなたのアプリケーションの |
||||
[エントリスクリプト](structure-entry-scripts.md) で定義可能な `YII_ENV` という名前の定数を提供します。 |
||||
たとえば: |
||||
|
||||
```php |
||||
defined('YII_ENV') or define('YII_ENV', 'dev'); |
||||
``` |
||||
|
||||
`YII_ENV` を次のいずれかの値と定義することができます: |
||||
|
||||
- `prod`: 本番環境。定数 `YII_ENV_PROD` は true と評価されます。 |
||||
とくに定義しない場合、これが `YII_ENV` のデフォルト値です。 |
||||
- `dev`: 開発環境。定数 `YII_ENV_DEV` は true と評価されます。 |
||||
- `test`: テスト環境。定数 `YII_ENV_TEST` は true と評価されます。 |
||||
|
||||
これらの環境定数を使用すると、現在の環境に基づいて条件付きで構成情報を指定することもできます。 |
||||
たとえば、アプリケーション構成情報には、開発環境での [デバッグツールバーとデバッガ](tool-debugger.md) |
||||
を有効にするために、次のコードを含むことができます。 |
||||
|
||||
```php |
||||
$config = [...]; |
||||
|
||||
if (YII_ENV_DEV) { |
||||
// 'dev' 環境用に構成情報を調整 |
||||
$config['bootstrap'][] = 'debug'; |
||||
$config['modules']['debug'] = 'yii\debug\Module'; |
||||
} |
||||
|
||||
return $config; |
||||
``` |
@ -0,0 +1,326 @@
|
||||
依存性注入コンテナ |
||||
============================== |
||||
|
||||
依存性注入 (DI) コンテナは、オブジェクトとそのすべての依存オブジェクトを、インスタンス化し、設定する方法を知っているオブジェクトです。 |
||||
なぜ DI コンテナが便利なのかは、[Martin の記事](http://martinfowler.com/articles/injection.html) の説明がわかりやすいでしょう。 |
||||
ここでは、主に Yii の提供する DI コンテナの使用方法を説明します。 |
||||
|
||||
|
||||
依存性注入 <a name="dependency-injection"></a> |
||||
-------------------- |
||||
|
||||
Yii は [[yii\di\Container]] クラスを通して DI コンテナの機能を提供します。これは、次の種類の依存性注入をサポートしています: |
||||
|
||||
* コンストラクタ·インジェクション |
||||
* セッター/プロパティ·インジェクション |
||||
* PHP コーラブル·インジェクション |
||||
|
||||
|
||||
### コンストラクタ·インジェクション <a name="constructor-injection"></a> |
||||
|
||||
DI コンテナは、コンストラクタパラメータの型ヒントの助けを借りた、コンストラクタ·インジェクションをサポートしています。 |
||||
型ヒントは、クラスやインタフェースが新しいオブジェクトの作成で使用されるさい、どれが依存であるのかということをコンテナに教えます。 |
||||
コンテナは、依存クラスやインタフェースのインスタンスを取得し、コンストラクタを通して、新しいオブジェクトにそれらの注入を試みます。 |
||||
たとえば |
||||
|
||||
```php |
||||
class Foo |
||||
{ |
||||
public function __construct(Bar $bar) |
||||
{ |
||||
} |
||||
} |
||||
|
||||
$foo = $container->get('Foo'); |
||||
// これは下記と等価: |
||||
$bar = new Bar; |
||||
$foo = new Foo($bar); |
||||
``` |
||||
|
||||
|
||||
### セッター/プロパティ·インジェクション <a name="setter-and-property-injection"></a> |
||||
|
||||
セッター/プロパティ·インジェクションは、[構成情報](concept-configurations.md) を通してサポートされます。 |
||||
依存関係を登録するときや、新しいオブジェクトを作成するとき、コンテナが使用する構成情報を提供することができ、 |
||||
それに対応するセッターまたはプロパティを通じて依存関係が注入されます。たとえば |
||||
|
||||
```php |
||||
use yii\base\Object; |
||||
|
||||
class Foo extends Object |
||||
{ |
||||
public $bar; |
||||
|
||||
private $_qux; |
||||
|
||||
public function getQux() |
||||
{ |
||||
return $this->_qux; |
||||
} |
||||
|
||||
public function setQux(Qux $qux) |
||||
{ |
||||
$this->_qux = $qux; |
||||
} |
||||
} |
||||
|
||||
$container->get('Foo', [], [ |
||||
'bar' => $container->get('Bar'), |
||||
'qux' => $container->get('Qux'), |
||||
]); |
||||
``` |
||||
|
||||
|
||||
### PHP コーラブル・インジェクション <a name="php-callable-injection"></a> |
||||
|
||||
この場合、コンテナは、登録された PHP のコーラブルオブジェクトを使用し、クラスの新しいインスタンスを構築します。 |
||||
コーラブルは、依存関係を解決し、新しく作成されたオブジェクトに適切にそれらを注入する責任があります。たとえば |
||||
|
||||
```php |
||||
$container->set('Foo', function () { |
||||
return new Foo(new Bar); |
||||
}); |
||||
|
||||
$foo = $container->get('Foo'); |
||||
``` |
||||
|
||||
|
||||
依存関係の登録 <a name="registering-dependencies"></a> |
||||
------------------------ |
||||
|
||||
あなたは、[[yii\di\Container::set()]] 使って依存関係を登録することができます。登録には依存関係の名前だけでなく、 |
||||
依存関係の定義が必要です。依存関係の名前は、クラス名、インタフェース名、エイリアス名を指定することができます。 |
||||
依存関係の定義には、クラス名、構成情報配列、PHPのコーラブルを指定できます。 |
||||
|
||||
```php |
||||
$container = new \yii\di\Container; |
||||
|
||||
// クラス名そのまま。これはなくてもかまいません。 |
||||
$container->set('yii\db\Connection'); |
||||
|
||||
// インターフェースの登録 |
||||
// クラスがインターフェースに依存する場合、対応するクラスが依存オブジェクトとしてインスタンス化されます |
||||
$container->set('yii\mail\MailInterface', 'yii\swiftmailer\Mailer'); |
||||
|
||||
// エイリアス名の登録。$container->get('foo') を使って Connection のインスタンスを作成できます |
||||
$container->set('foo', 'yii\db\Connection'); |
||||
|
||||
// 構成情報をともなうクラスの登録。クラスが get() でインスタンス化されるとき構成情報が適用されます |
||||
$container->set('yii\db\Connection', [ |
||||
'dsn' => 'mysql:host=127.0.0.1;dbname=demo', |
||||
'username' => 'root', |
||||
'password' => '', |
||||
'charset' => 'utf8', |
||||
]); |
||||
|
||||
// クラスの構成情報をともなうエイリアス名の登録 |
||||
// この場合、クラスを指定する "class" 要素が必要です |
||||
$container->set('db', [ |
||||
'class' => 'yii\db\Connection', |
||||
'dsn' => 'mysql:host=127.0.0.1;dbname=demo', |
||||
'username' => 'root', |
||||
'password' => '', |
||||
'charset' => 'utf8', |
||||
]); |
||||
|
||||
// PHP コーラブルの登録 |
||||
// このコーラブルは $container->get('db') が呼ばれるたびに実行されます |
||||
$container->set('db', function ($container, $params, $config) { |
||||
return new \yii\db\Connection($config); |
||||
}); |
||||
|
||||
// コンポーネントインスタンスの登録 |
||||
// $container->get('pageCache') は呼ばれるたびに毎回同じインスタンスを返します |
||||
$container->set('pageCache', new FileCache); |
||||
``` |
||||
|
||||
> 補足: 依存関係名が、対応する依存関係の定義と同じである場合は、それを DI コンテナに登録する必要はありません。 |
||||
|
||||
`set()` を介して登録された依存性は、依存性が必要とされるたびにインスタンスを生成します。 |
||||
[[yii\di\Container::setSingleton()]] を使うと、単一のインスタンスをひとつだけ生成する依存関係を登録することができます: |
||||
|
||||
```php |
||||
$container->setSingleton('yii\db\Connection', [ |
||||
'dsn' => 'mysql:host=127.0.0.1;dbname=demo', |
||||
'username' => 'root', |
||||
'password' => '', |
||||
'charset' => 'utf8', |
||||
]); |
||||
``` |
||||
|
||||
|
||||
依存関係の解決 <a name="resolving-dependencies"></a> |
||||
---------------------- |
||||
|
||||
依存関係を登録すると、新しいオブジェクトを作成するのに DI コンテナを使用することができ、 |
||||
コンテナが自動的に、依存性をインスタンス化して新しく作成されたオブジェクトに注入することで、 |
||||
依存関係を解決します。依存関係の解決は再帰的、つまり、ある依存性が他の依存関係を持っている場合、 |
||||
それらの依存関係も自動的に解決されます。 |
||||
|
||||
[[yii\di\Container::get()]] を使って、新しいオブジェクトを作成することができます。 |
||||
このメソッドは、クラス名、インタフェース名、エイリアス名で指定できる依存関係の名前を受け取ります。 |
||||
依存関係名は、 `set()` や `setSingleton()` を介して登録されていたりされていなかったりする |
||||
可能性があります。オプションで、クラスのコンストラクタのパラメータのリストや、新しく作成された |
||||
オブジェクトを設定するための [設定情報](concept-configurations.md) を渡すことができます。 |
||||
たとえば |
||||
|
||||
```php |
||||
// "db" は事前に登録されたエイリアス名 |
||||
$db = $container->get('db'); |
||||
|
||||
// これと同じ意味: $engine = new \app\components\SearchEngine($apiKey, ['type' => 1]); |
||||
$engine = $container->get('app\components\SearchEngine', [$apiKey], ['type' => 1]); |
||||
``` |
||||
|
||||
見えないところで、DIコンテナは、単に新しいオブジェクトを作成するよりもはるかに多くの作業を行います。 |
||||
コンテナは、最初の依存クラスまたはインタフェースの名前を見つけるために、クラスのコンストラクタを検査し、 |
||||
自動的にそれらの依存関係を再帰で解決します。 |
||||
|
||||
次のコードでより洗練された例を示します。 `UserLister` クラスは `UserFinderInterface` |
||||
インタフェースを実装するオブジェクトに依存します。 `UserFinder` クラスはこのインターフェイスを実装していて、かつ、 |
||||
`Connection` オブジェクトに依存します。これらのすべての依存関係は、クラスのコンストラクタのパラメータのタイプヒンティングで宣言されています。 |
||||
プロパティ依存性の登録をすれば、DI コンテナは自動的にこれらの依存関係を解決し、単純に `get('userLister')` |
||||
を呼び出すだけで新しい `UserLister` インスタンスを作成できます。 |
||||
|
||||
```php |
||||
namespace app\models; |
||||
|
||||
use yii\base\Object; |
||||
use yii\db\Connection; |
||||
use yii\di\Container; |
||||
|
||||
interface UserFinderInterface |
||||
{ |
||||
function findUser(); |
||||
} |
||||
|
||||
class UserFinder extends Object implements UserFinderInterface |
||||
{ |
||||
public $db; |
||||
|
||||
public function __construct(Connection $db, $config = []) |
||||
{ |
||||
$this->db = $db; |
||||
parent::__construct($config); |
||||
} |
||||
|
||||
public function findUser() |
||||
{ |
||||
} |
||||
} |
||||
|
||||
class UserLister extends Object |
||||
{ |
||||
public $finder; |
||||
|
||||
public function __construct(UserFinderInterface $finder, $config = []) |
||||
{ |
||||
$this->finder = $finder; |
||||
parent::__construct($config); |
||||
} |
||||
} |
||||
|
||||
$container = new Container; |
||||
$container->set('yii\db\Connection', [ |
||||
'dsn' => '...', |
||||
]); |
||||
$container->set('app\models\UserFinderInterface', [ |
||||
'class' => 'app\models\UserFinder', |
||||
]); |
||||
$container->set('userLister', 'app\models\UserLister'); |
||||
|
||||
$lister = $container->get('userLister'); |
||||
|
||||
// と、いうのはこれと同じ: |
||||
|
||||
$db = new \yii\db\Connection(['dsn' => '...']); |
||||
$finder = new UserFinder($db); |
||||
$lister = new UserLister($finder); |
||||
``` |
||||
|
||||
|
||||
実際の使いかた <a name="practical-usage"></a> |
||||
--------------- |
||||
|
||||
あなたのアプリケーションの [エントリスクリプト](structure-entry-scripts.md) で `Yii.php` ファイルをインクルードするとき、 |
||||
Yii は DI コンテナを作成します。この DI コンテナは [[Yii::$container]] を介してアクセス可能です。 [[Yii::createObject()]] を呼び出したとき、 |
||||
このメソッドは実際には、新しいオブジェクトを作成ために、コンテナの [[yii\di\Container::get()|get()]] メソッドを呼び出しています。 |
||||
前述のとおり、DI コンテナは(もしあれば)自動的に依存関係を解決し、新しく作成されたオブジェクトにそれらを注入します。 |
||||
Yii は、新しいオブジェクトを作成するさいそのコアコードのほとんどで [[Yii::createObject()]] を使用しているため、これは、 |
||||
[[Yii::$container]] を扱えばグローバルにオブジェクトをカスタマイズすることができることを意味しています。 |
||||
|
||||
たとえば、 [[yii\widgets\LinkPager]] のページネーションボタンのデフォルト個数をグローバルにカスタマイズすることができます: |
||||
|
||||
```php |
||||
\Yii::$container->set('yii\widgets\LinkPager', ['maxButtonCount' => 5]); |
||||
``` |
||||
|
||||
次のコードでビューでウィジェットを使用すれば、 `maxButtonCount` プロパティは、 |
||||
クラスで定義されているデフォルト値 10 の代わりに 5 で初期化されます。 |
||||
|
||||
```php |
||||
echo \yii\widgets\LinkPager::widget(); |
||||
``` |
||||
|
||||
DIコンテナを経由して設定された値は、こうやって、まだまだ上書きすることができます: |
||||
|
||||
```php |
||||
echo \yii\widgets\LinkPager::widget(['maxButtonCount' => 20]); |
||||
``` |
||||
|
||||
DI コンテナの自動コンストラクタ・インジェクションの利点を活かす別の例です。 |
||||
あなたのコントローラクラスが、ホテル予約サービスのような、いくつかの他のオブジェクトに依存するとします。 |
||||
あなたは、コンストラクタパラメータを通して依存関係を宣言して、DI コンテナにあなたの課題を解決させることができます。 |
||||
|
||||
```php |
||||
namespace app\controllers; |
||||
|
||||
use yii\web\Controller; |
||||
use app\components\BookingInterface; |
||||
|
||||
class HotelController extends Controller |
||||
{ |
||||
protected $bookingService; |
||||
|
||||
public function __construct($id, $module, BookingInterface $bookingService, $config = []) |
||||
{ |
||||
$this->bookingService = $bookingService; |
||||
parent::__construct($id, $module, $config); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
あなたがブラウザからこのコントローラにアクセスすると、 `BookingInterface` をインスタンス化できませんという |
||||
不具合報告エラーが表示されるでしょう。これは、この依存関係に対処する方法を DI コンテナに教える必要があるからです: |
||||
|
||||
```php |
||||
\Yii::$container->set('app\components\BookingInterface', 'app\components\BookingService'); |
||||
``` |
||||
|
||||
これで、あなたが再びコントローラにアクセスするときは、 `app\components\BookingService` |
||||
のインスタンスが作成され、コントローラのコンストラクタに3番目のパラメータとして注入されるようになります。 |
||||
|
||||
|
||||
依存関係を登録するときに <a name="when-to-register-dependencies"></a> |
||||
----------------------------- |
||||
|
||||
依存関係は、新しいオブジェクトが作成されるとき必要とされるので、それらの登録は可能な限り早期に行われるべきです。 |
||||
推奨プラクティス以下のとおりです: |
||||
|
||||
* あなたがアプリケーションの開発者である場合、アプリケーションの [エントリスクリプト](structure-entry-scripts.md) 内、 |
||||
またはエントリスクリプトにインクルードされるスクリプト内で、依存関係を登録することができます。 |
||||
* あなたが再配布可能な [エクステンション](structure-extensions.md) の開発者である場合は、エクステンションのブートストラップクラス内で |
||||
依存関係を登録することができます。 |
||||
|
||||
|
||||
まとめ <a name="summary"></a> |
||||
------- |
||||
|
||||
依存性注入と [サービスロケータ](concept-service-locator.md) はともに、疎結合でよりテストしやすい方法でのソフトウェア構築を可能にする、 |
||||
定番のデザインパターンです。依存性注入とサービスロケータへのより深い理解を得るために、 [Martin の記事](http://martinfowler.com/articles/injection.html) |
||||
を読むことを強くお勧めします。 |
||||
|
||||
Yiiはその [サービスロケータ](concept-service-locator.md) を、依存性注入(DI)コンテナの上に実装しています。 |
||||
サービスロケータは、新しいオブジェクトのインスタンスを作成しようとしたとき、DI コンテナに呼び出しを転送します。 |
||||
後者は、依存関係を、上で説明したように自動的に解決します。 |
||||
|
@ -0,0 +1,279 @@
|
||||
イベント |
||||
====== |
||||
|
||||
イベントを使うと、既存のコードの特定の実行ポイントに、カスタムコードを挿入することができます。イベントにカスタムコードを添付すると、 |
||||
イベントがトリガされたときにコードが自動的に実行されます。たとえば、メーラーオブジェクトがメッセージを正しく送信できたとき、 |
||||
`messageSent` イベントをトリガするとします。もしメッセージの送信がうまく行ったことを知りたければ、単に `messageSent` |
||||
イベントにトラッキングコードを付与すするだけで、それが可能になります。 |
||||
|
||||
Yiiはイベントをサポートするために、 [[yii\base\Component]] と呼ばれる基底クラスを導入してします。クラスがイベントをトリガする必要がある場合は、 |
||||
[[yii\base\Component]] もしくはその子クラスを継承する必要があります。 |
||||
|
||||
|
||||
イベントハンドラ <a name="event-handlers"></a> |
||||
-------------- |
||||
|
||||
イベントハンドラとは、関連するイベントがトリガされたときに実行される、 [PHP コールバック](http://www.php.net/manual/en/language.types.callable.php) |
||||
です。次のコールバックのいずれも使用可能です: |
||||
|
||||
- 文字列で指定されたグローバル PHP 関数 (括弧を除く) `'trim'` など |
||||
- オブジェクトとメソッド名文字列の配列で指定された、オブジェクトのメソッド (括弧を除く) `[$object, 'methodName']` など |
||||
- クラス名文字列とメソッド名文字列の配列で指定された、静的なクラスメソッド `[$class, 'methodName']` など |
||||
- 無名関数 `function ($event) { ... }` など |
||||
|
||||
イベントハンドラのシグネチャはこのようになります: |
||||
|
||||
```php |
||||
function ($event) { |
||||
// $event は yii\base\Event またはその子クラスのオブジェクト |
||||
} |
||||
``` |
||||
|
||||
`$event` パラメータを介して、イベントハンドラは発生したイベントに関して次の情報を得ることができます: |
||||
|
||||
- [[yii\base\Event::name|イベント名]] |
||||
- [[yii\base\Event::sender|イベント送信元]]: `trigger()` メソッドを呼び出したオブジェクト |
||||
- [[yii\base\Event::data|カスタムデータ]]: イベントハンドラを接続するときに提供されたデータ (後述) |
||||
|
||||
|
||||
イベントハンドラのアタッチ <a name="attaching-event-handlers"></a> |
||||
------------------------ |
||||
|
||||
イベントハンドラは [[yii\base\Component::on()]] を呼び出すことでアタッチできます。たとえば: |
||||
|
||||
```php |
||||
$foo = new Foo; |
||||
|
||||
// このハンドラはグローバル関数です |
||||
$foo->on(Foo::EVENT_HELLO, 'function_name'); |
||||
|
||||
// このハンドラはオブジェクトのメソッドです |
||||
$foo->on(Foo::EVENT_HELLO, [$object, 'methodName']); |
||||
|
||||
// このハンドラは静的なクラスメソッドです |
||||
$foo->on(Foo::EVENT_HELLO, ['app\components\Bar', 'methodName']); |
||||
|
||||
// このハンドラは無名関数です |
||||
$foo->on(Foo::EVENT_HELLO, function ($event) { |
||||
// イベント処理ロジック |
||||
}); |
||||
``` |
||||
|
||||
また、 [構成情報](concept-configurations.md) を通じてイベントハンドラをアタッチすることもできます。詳細については |
||||
[構成情報](concept-configurations.md) の章を参照してください。 |
||||
|
||||
イベントハンドラをアタッチするとき、 [[yii\base\Component::on()]] の3番目のパラメータとして、付加的なデータを提供することができます。 |
||||
そのデータは、イベントがトリガされてハンドラが呼び出されるときに、ハンドラ内で利用きます。たとえば: |
||||
|
||||
```php |
||||
// 次のコードはイベントがトリガされたとき "abc" を表示します |
||||
// "on" に3番目の引数として渡されたデータを $event->data が保持しているからです |
||||
$foo->on(Foo::EVENT_HELLO, 'function_name', 'abc'); |
||||
|
||||
function function_name($event) { |
||||
echo $event->data; |
||||
} |
||||
``` |
||||
|
||||
イベントハンドラの順序 |
||||
------------------- |
||||
|
||||
ひとつのイベントには、ひとつだけでなく複数のハンドラをアタッチすることができます。イベントがトリガされると、アタッチされたハンドラは、 |
||||
それらがイベントにアタッチされた順序どおりに呼び出されます。あるハンドラがその後に続くハンドラの呼び出しを停止する必要がある場合は、 |
||||
`$event` パラメータの [[yii\base\Event::handled]] プロパティを true に設定します: |
||||
|
||||
```php |
||||
$foo->on(Foo::EVENT_HELLO, function ($event) { |
||||
$event->handled = true; |
||||
}); |
||||
``` |
||||
|
||||
デフォルトでは、新たに接続されたハンドラは、イベントの既存のハンドラのキューに追加されます。その結果、 |
||||
イベントがトリガされたとき、そのハンドラは一番最後に呼び出されます。もし、そのハンドラが最初に呼び出されるよう、 |
||||
ハンドラのキューの先頭に新しいハンドラを挿入したい場合は、[[yii\base\Component::on()]] を呼び出とき、4番目のパラメータ `$append` に false を渡します: |
||||
|
||||
```php |
||||
$foo->on(Foo::EVENT_HELLO, function ($event) { |
||||
// ... |
||||
}, $data, false); |
||||
``` |
||||
|
||||
イベントのトリガー <a name="triggering-events"></a> |
||||
----------------- |
||||
|
||||
イベントは、 [[yii\base\Component::trigger()]] メソッドを呼び出すことでトリガされます。このメソッドには **イベント名** が必須で、 |
||||
オプションで、イベントハンドラに渡されるパラメータを記述したイベントオブジェクトを渡すこともできます。たとえば: |
||||
|
||||
```php |
||||
namespace app\components; |
||||
|
||||
use yii\base\Component; |
||||
use yii\base\Event; |
||||
|
||||
class Foo extends Component |
||||
{ |
||||
const EVENT_HELLO = 'hello'; |
||||
|
||||
public function bar() |
||||
{ |
||||
$this->trigger(self::EVENT_HELLO); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
上記のコードでは、すべての `bar()` の呼び出しは、 `hello` という名前のイベントをトリガします。 |
||||
|
||||
> Tip: イベント名を表すときはクラス定数を使用することをお勧めします。上記の例では、定数 `EVENT_HELLO` は |
||||
`hello` イベントを表しています。このアプローチには 3 つの利点があります。まず、タイプミスを防ぐことができます。次に、IDE の自動補完サポートでイベントを |
||||
認識できるようになります。第 3 に、クラスでどんなイベントがサポートされているかを表したいとき、定数の宣言をチェックするだけで済みます。 |
||||
|
||||
イベントをトリガするとき、イベントハンドラに追加情報を渡したいことがあります。たとえば、メーラーが `messageSent` イベントのハンドラに |
||||
メッセージ情報を渡して、ハンドラが送信されたメッセージの詳細を知ることができるようにしたいかもしれません。 |
||||
これを行うために、 [[yii\base\Component::trigger()]] メソッドの2番目のパラメータとして、イベントオブジェクトを与えることができます。 |
||||
イベントオブジェクトは [[yii\base\Event]] クラスあるいはその子クラスのインスタンスでなければなりません。たとえば: |
||||
|
||||
```php |
||||
namespace app\components; |
||||
|
||||
use yii\base\Component; |
||||
use yii\base\Event; |
||||
|
||||
class MessageEvent extends Event |
||||
{ |
||||
public $message; |
||||
} |
||||
|
||||
class Mailer extends Component |
||||
{ |
||||
const EVENT_MESSAGE_SENT = 'messageSent'; |
||||
|
||||
public function send($message) |
||||
{ |
||||
// ... $message 送信 ... |
||||
|
||||
$event = new MessageEvent; |
||||
$event->message = $message; |
||||
$this->trigger(self::EVENT_MESSAGE_SENT, $event); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
[[yii\base\Component::trigger()]] メソッドが呼び出されたとき、この名前を付けられたイベントに |
||||
アタッチされたハンドラがすべて呼び出されます。 |
||||
|
||||
|
||||
イベントハンドラのデタッチ <a name="detaching-event-handlers"></a> |
||||
------------------------ |
||||
|
||||
イベントからハンドラを取り外すには、 [[yii\base\Component::off()]] メソッドを呼び出します。たとえば: |
||||
|
||||
```php |
||||
// このハンドラはグローバル関数です |
||||
$foo->off(Foo::EVENT_HELLO, 'function_name'); |
||||
|
||||
// このハンドラはオブジェクトのメソッドです |
||||
$foo->off(Foo::EVENT_HELLO, [$object, 'methodName']); |
||||
|
||||
// このハンドラは静的なクラスメソッドです |
||||
$foo->off(Foo::EVENT_HELLO, ['app\components\Bar', 'methodName']); |
||||
|
||||
// このハンドラは無名関数です |
||||
$foo->off(Foo::EVENT_HELLO, $anonymousFunction); |
||||
``` |
||||
|
||||
一般的には、イベントにアタッチされたときどこかに保存してある場合を除き、無名関数を取り外そうとはしないでください。 |
||||
上記の例は、無名関数は変数 `$anonymousFunction` として保存されていたものとしています。 |
||||
|
||||
イベントからすべてのハンドラを取り外すには、単純に、第 2 パラメータを指定せずに [[yii\base\Component::off()]] を呼び出します。 |
||||
|
||||
```php |
||||
$foo->off(Foo::EVENT_HELLO); |
||||
``` |
||||
|
||||
|
||||
クラスレベル・イベントハンドラ <a name="class-level-event-handlers"></a> |
||||
-------------------------- |
||||
|
||||
ここまでの項では、 *インスタンスレベル* でのイベントにハンドラをアタッチする方法を説明してきました。 |
||||
場合によっては、特定のインスタンスだけではなく、クラスのすべてのインスタンスがトリガした |
||||
イベントに応答したいことがあります。すべてのインスタンスにイベントハンドラをアタッチする代わりに、静的メソッド |
||||
[[yii\base\Event::on()]] を呼び出すことで、 *クラスレベル* でハンドラをアタッチすることができます。 |
||||
|
||||
たとえば、[アクティブレコード](db-active-record.md) オブジェクトは、データベースに新しいレコードを挿入するたびに、 |
||||
[[yii\db\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] イベントをトリガします。 *すべての* |
||||
[アクティブレコード](db-active-record.md) オブジェクトによって行われる挿入を追跡するには、次のコードが使えます: |
||||
|
||||
```php |
||||
use Yii; |
||||
use yii\base\Event; |
||||
use yii\db\ActiveRecord; |
||||
|
||||
Event::on(ActiveRecord::className(), ActiveRecord::EVENT_AFTER_INSERT, function ($event) { |
||||
Yii::trace(get_class($event->sender) . ' が挿入されました'); |
||||
}); |
||||
``` |
||||
|
||||
[[yii\db\ActiveRecord|ActiveRecord]] またはその子クラスのいずれかが、 [[yii\db\BaseActiveRecord::EVENT_AFTER_INSERT|EVENT_AFTER_INSERT]] |
||||
をトリガーするといつでも、このイベントハンドラが呼び出されます。ハンドラの中では、 `$event->sender` を通して、 |
||||
イベントをトリガしたオブジェクトを取得することができます。 |
||||
|
||||
オブジェクトがイベントをトリガするときは、最初にインスタンスレベルのハンドラを呼び出し、続いてクラスレベルのハンドラとなります。 |
||||
|
||||
静的メソッド [[yii\base\Event::trigger()]] を呼び出すことによって、 *クラスレベル* でイベントをトリガすることができます。 |
||||
クラスレベルでのイベントは、特定のオブジェクトに関連付けられていません。そのため、これはクラスレベルのイベントハンドラだけを |
||||
呼び出します。たとえば: |
||||
|
||||
```php |
||||
use yii\base\Event; |
||||
|
||||
Event::on(Foo::className(), Foo::EVENT_HELLO, function ($event) { |
||||
echo $event->sender; // "app\models\Foo" を表示 |
||||
}); |
||||
|
||||
Event::trigger(Foo::className(), Foo::EVENT_HELLO); |
||||
``` |
||||
|
||||
この場合、`$event->sender` は、オブジェクトインスタンスではなく、イベントをトリガーするクラスの名前を指すことに注意してください。 |
||||
|
||||
> 注: クラスレベルのハンドラは、そのクラスのあらゆるインスタンス、またはあらゆる子クラスのインスタンスがトリガしたイベントに応答 |
||||
してしまうため、よく注意して使わなければなりません。 [[yii\base\Object]] のように、クラスが低レベルの基底クラスの場合は特にそうです。 |
||||
|
||||
クラスレベルのイベントハンドラを取り外すときは、 [[yii\base\Event::off()]] を呼び出します。たとえば: |
||||
|
||||
```php |
||||
// $handler をデタッチ |
||||
Event::off(Foo::className(), Foo::EVENT_HELLO, $handler); |
||||
|
||||
// Foo::EVENT_HELLO のすべてのハンドラをデタッチ |
||||
Event::off(Foo::className(), Foo::EVENT_HELLO); |
||||
``` |
||||
|
||||
|
||||
グローバル・イベント <a name="global-events"></a> |
||||
------------- |
||||
|
||||
Yiiは、実際に上記のイベントメカニズムに基づいたトリックである、いわゆる *グローバル・イベント* をサポートしています。 |
||||
グローバル・イベントは、 [アプリケーション](structure-applications.md) インスタンス自身などの、グローバルにアクセス可能なシングルトンを必要とします。 |
||||
|
||||
グローバルイベントを作成するには、イベント送信者は、送信者の自前の `trigger()` メソッドを呼び出す代わりに、シングルトンの |
||||
`trigger()` メソッドを呼び出してイベントをトリガします。同じく、イベントハンドラも、シングルトンのイベントにアタッチされます。たとえば: |
||||
|
||||
```php |
||||
use Yii; |
||||
use yii\base\Event; |
||||
use app\components\Foo; |
||||
|
||||
Yii::$app->on('bar', function ($event) { |
||||
echo get_class($event->sender); // "app\components\Foo" を表示 |
||||
}); |
||||
|
||||
Yii::$app->trigger('bar', new Event(['sender' => new Foo])); |
||||
``` |
||||
|
||||
グローバルイベントを使用する利点は、オブジェクトによってトリガされるイベントハンドラを設けたいとき、オブジェクトがなくてもいい |
||||
ということです。その代わりに、ハンドラのアタッチとイベントのトリガはともに、(アプリケーションのインスタンスなど) シングルトンを |
||||
介して行われます。 |
||||
|
||||
しかし、グローバルイベントの名前空間はあらゆる部分から共有されているので、名前空間の整理 ("frontend.mail.sent"、"backend.mail.sent" など) |
||||
を導入するというような、賢いグローバルイベントの名前付けをする必要があります。 |
||||
|
@ -0,0 +1,83 @@
|
||||
サービスロケータ |
||||
=============== |
||||
|
||||
サービスロケータは、アプリケーションが必要とする可能性のある各種のサービス (またはコンポーネント) を提供する方法を知っているオブジェクトです。 |
||||
サービスロケータ内では、各コンポーネントは単一のインスタンスとして存在し、IDによって一意に識別されます。 |
||||
あなたは、このIDを使用してサービスロケータからコンポーネントを取得できます。 |
||||
|
||||
Yii では、サービスロケータは単純に [[yii\di\ServiceLocator]] のインスタンス、または子クラスのインスタンスです。 |
||||
|
||||
Yii の中で最も一般的に使用されるサービスロケータは、 *アプリケーション* オブジェクトで、 `\Yii::$app` |
||||
を通じてアクセスできます。それが提供するサービスは、 *アプリケーションコンポーネント* と呼ばれ、それは `request` 、 |
||||
`response`、 `urlManager` のようなコンポーネントです。あなたはサービスロケータによって提供される機能を通じて、 |
||||
簡単に、これらのコンポーネントを構成、あるいは独自の実装に置き換え、といったことができます。 |
||||
|
||||
アプリケーションオブジェクトの他に、各モジュールオブジェクトもまたサービスロケータです。 |
||||
|
||||
サービスロケータを使用する最初のステップは、コンポーネントを登録することです。コンポーネントは、 [[yii\di\ServiceLocator::set()]] |
||||
を通じて登録することができます。次のコードは、コンポーネントを登録するさまざまな方法を示しています。 |
||||
|
||||
```php |
||||
use yii\di\ServiceLocator; |
||||
use yii\caching\FileCache; |
||||
|
||||
$locator = new ServiceLocator; |
||||
|
||||
// コンポーネントの作成に使われるクラス名を使用して "cache" を登録 |
||||
$locator->set('cache', 'yii\caching\ApcCache'); |
||||
|
||||
// コンポーネントの作成に使われる構成情報配列を使用して "db" を登録 |
||||
$locator->set('db', [ |
||||
'class' => 'yii\db\Connection', |
||||
'dsn' => 'mysql:host=localhost;dbname=demo', |
||||
'username' => 'root', |
||||
'password' => '', |
||||
]); |
||||
|
||||
// コンポーネントを構築する匿名関数を使って "search" を登録 |
||||
$locator->set('search', function () { |
||||
return new app\components\SolrService; |
||||
}); |
||||
|
||||
// コンポーネントを使って "pageCache" を登録 |
||||
$locator->set('pageCache', new FileCache); |
||||
``` |
||||
|
||||
いったんコンポーネントが登録されたら、次の 2 つの方法のいずれかで、その ID を使ってそれにアクセスすることができます: |
||||
|
||||
```php |
||||
$cache = $locator->get('cache'); |
||||
// または代わりに |
||||
$cache = $locator->cache; |
||||
``` |
||||
|
||||
以上のように、 [[yii\di\ServiceLocator]] はコンポーネント ID を使用したプロパティのように、コンポーネントにアクセスすることができます。 |
||||
あなたが最初にコンポーネントにアクセスしたとき、 [[yii\di\ServiceLocator]] はコンポーネントの登録情報を使用してコンポーネントの新しいインスタンスを作成し、 |
||||
それを返します。後でそのコンポーネントが再度アクセスされた場合、サービスロケータは同じインスタンスを返します。 |
||||
|
||||
[[yii\di\ServiceLocator::has()]] を使って、コンポーネント ID がすでに登録されているかをチェックできます。 |
||||
無効なIDで [[yii\di\ServiceLocator::get()]] を呼び出した場合、例外がスローされます。 |
||||
|
||||
サービスロケータは多くの場合、 [構成情報](concept-configurations.md) で作成されるため、 |
||||
[[yii\di\ServiceLocator::setComponents()|components]] という名前の書き込み可能プロパティが提供されています。 |
||||
これで一度に複数のコンポーネントを設定して登録することができます。次のコードはアプリケーションを構成する構成情報配列を示しており、 |
||||
"db" と "cache" と "search" コンポーネントの登録もしています: |
||||
|
||||
```php |
||||
return [ |
||||
// ... |
||||
'components' => [ |
||||
'db' => [ |
||||
'class' => 'yii\db\Connection', |
||||
'dsn' => 'mysql:host=localhost;dbname=demo', |
||||
'username' => 'root', |
||||
'password' => '', |
||||
], |
||||
'cache' => 'yii\caching\ApcCache', |
||||
'search' => function () { |
||||
return new app\components\SolrService; |
||||
}, |
||||
], |
||||
]; |
||||
``` |
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,6 @@
|
||||
Elasticsearch |
||||
============= |
||||
|
||||
> Note|注意: この節はまだ執筆中です。 |
||||
> |
||||
> まだ内容がありません。 |
@ -0,0 +1,361 @@
|
||||
データベースマイグレーション |
||||
============================ |
||||
|
||||
> Note|注意: この節はまだ執筆中です。 |
||||
|
||||
ソースコードと同じように、データベースの構造も、データベース駆動型のアプリケーションが開発され保守されるにともなって徐々に発展していきます。 |
||||
例えば、開発中に新しいテーブルが追加されることもあるでしょうし、アプリケーションが実運用に移行した後になって追加のインデックスが必要であることが発見されることもあるでしょう。 |
||||
このようなデータベースの構造的な変更 (**マイグレーション** と呼ばれます) を追跡記録することが重要であるのは、ソースコードに対する変更がバージョン管理を使って追跡記録されるのと全く同じことです。 |
||||
ソースコードとデータベースの同期が失われると、バグが発生するか、アプリケーション全体が動かなくなるかします。 |
||||
こうした理由によって、データベースマイグレーションツールを提供して、データベースマイグレーションの履歴の追跡管理、新しいマイグレーションの適用、また、既存のマイグレーションの取消が出来るようにしています。 |
||||
|
||||
下記のステップは、開発中にチームによってデータベースマイグレーションが使用される例を示すものです。 |
||||
|
||||
1. Tim が新しいマイグレーション (例えば、新しいテーブルを作成したり、カラムの定義を変更したりなど) を作る。 |
||||
2. Tim が新しいマイグレーションをソースコントロールシステム (例えば Git や Mercurial) にコミットする。 |
||||
3. Doug がソースコントロールシステムから自分のレポジトリを更新して新しいマイグレーションを受け取る。 |
||||
4. Doug がマイグレーションを彼のローカルの開発用データベースに適用し、Tim が行った変更を反映して、自分のデータベースを同期する。 |
||||
|
||||
Yii はデータベースマイグレーションを `yii migrate` コマンドラインツールによってサポートします。 |
||||
このツールは、以下の機能をサポートしています。 |
||||
|
||||
* 新しいマイグレーションの作成 |
||||
* マイグレーションの適用、取消、再適用 |
||||
* マイグレーションの履歴と新規マイグレーションの閲覧 |
||||
|
||||
|
||||
マイグレーションを作成する |
||||
-------------------------- |
||||
|
||||
新しいマイグレーションを作成するためには、次のコマンドを実行します。 |
||||
|
||||
``` |
||||
yii migrate/create <name> |
||||
``` |
||||
|
||||
要求される `name` パラメータには、マイグレーションの非常に短い説明を指定します。 |
||||
例えば、マイグレーションが *news* という名前のテーブルを作成するものである場合は、コマンドを次のようにして使います。 |
||||
|
||||
``` |
||||
yii migrate/create create_news_table |
||||
``` |
||||
|
||||
すぐ後で説明するように、マイグレーションでは、この `name` パラメータは PHP のクラス名の一部として使用されます。 |
||||
したがって、アルファベット、数字、および/または、アンダースコアだけを含まなければなりません。 |
||||
|
||||
上記のコマンドは、`m101129_185401_create_news_table.php` という名前の新しいファイルを作成します。 |
||||
このファイルは `@app/migrations` ディレクトリに作成されます。 |
||||
初期状態では、このマイグレーションファイルは以下のコードを含んでいます。 |
||||
|
||||
```php |
||||
class m101129_185401_create_news_table extends \yii\db\Migration |
||||
{ |
||||
public function up() |
||||
{ |
||||
} |
||||
|
||||
public function down() |
||||
{ |
||||
echo "m101129_185401_create_news_table cannot be reverted.\n"; |
||||
return false; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
クラス名はファイル名と同じであり、`m<timestamp>_<name>` というパターンに従います。ここで、 |
||||
|
||||
* `<timestamp>` は、マイグレーションが作成された (`yymmdd_hhmmss` という書式の) UTC タイムスタンプであり、 |
||||
* `<name>` は、コマンドの `name` パラメータから取られた文字列です。 |
||||
|
||||
クラスの中では、`up()` メソッドが、実際のデータベースマイグレーションを実装するコードを含むべきメソッドです。 |
||||
言い換えると、`up()` メソッドが実際にデータベースを変更するコードを実行します。 |
||||
`down()` メソッドは、`up()` によって加えられた変更を取り消すコードを含むことが出来ます。 |
||||
|
||||
場合によっては、`down()` がデータベースマイグレーションを取り消すことが出来ないことがあります。 |
||||
例えば、マイグレーションがテーブルの行やテーブル全体を削除した場合は、そのデータを `down()` メソッドで復旧することは出来ません。 |
||||
そのような場合には、マイグレーションは不可逆であると呼ばれ、データベースを以前の状態にロールバックすることは出来ません。 |
||||
マイグレーションが不可逆である場合は、生成された上記のコードのように、`down()` メソッドは `false` を返して、マイグレーションが取り消せないものであることを示します。 |
||||
|
||||
例として、新しいテーブルを作成するマイグレーションを示しましょう。 |
||||
|
||||
```php |
||||
|
||||
use yii\db\Schema; |
||||
|
||||
class m101129_185401_create_news_table extends \yii\db\Migration |
||||
{ |
||||
public function up() |
||||
{ |
||||
$this->createTable('news', [ |
||||
'id' => 'pk', |
||||
'title' => Schema::TYPE_STRING . ' NOT NULL', |
||||
'content' => Schema::TYPE_TEXT, |
||||
]); |
||||
} |
||||
|
||||
public function down() |
||||
{ |
||||
$this->dropTable('news'); |
||||
} |
||||
|
||||
} |
||||
``` |
||||
|
||||
基底クラスである [[\yii\db\Migration]] が、データベース接続を `db` プロパティによって提供しています。 |
||||
これを使って、データベースのデータとスキーマを操作することが出来ます。 |
||||
|
||||
この例で使われているカラムのタイプは抽象的なタイプであり、Yii によって、あなたが使用するデータベース管理システムに応じて、対応するタイプに置き換えられます。 |
||||
抽象的なタイプを使うと、データベースに依存しないマイグレーションを書くことが出来ます。 |
||||
例えば、`pk` は、MySQL では `int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY` に置き換えられ、sqlite では `integer PRIMARY KEY AUTOINCREMENT NOT NULL` に置き換えられます。 |
||||
さらに詳細な説明と、利用可能なタイプについては、[[yii\db\QueryBuilder::getColumnType()]] のドキュメントを参照してください。 |
||||
また、カラムのタイプを定義するのに、[[yii\db\Schema]] で定義されている定数を使うことも出来ます。 |
||||
|
||||
> Note|注意: テーブル定義の最後に、単純な文字列として指定された、制約やその他の特別なテーブルオプションを追加することが出来ます。 |
||||
> 例えば、上記のマイグレーションでは、`content` 属性の定義の後に、`'CONSTRAINT ...'` やその他の特別なオプションを書くことが出来ます。 |
||||
|
||||
|
||||
トランザクションを使うマイグレーション |
||||
-------------------------------------- |
||||
|
||||
複雑な DB マイグレーションを実行するときは、通常、データベースの一貫性と整合性を保つために、個々のマイグレーションが全体として成功または失敗することを保証する必要があります。 |
||||
この目的を達成するために、DB トランザクションを利用することが出来ます。 |
||||
この目的のためには、`safeUp` と `safeDown` という特別なメソッドを使います。 |
||||
|
||||
```php |
||||
|
||||
use yii\db\Schema; |
||||
|
||||
class m101129_185401_create_news_table extends \yii\db\Migration |
||||
{ |
||||
public function safeUp() |
||||
{ |
||||
$this->createTable('news', [ |
||||
'id' => 'pk', |
||||
'title' => Schema::TYPE_STRING . ' NOT NULL', |
||||
'content' => Schema::TYPE_TEXT, |
||||
]); |
||||
|
||||
$this->createTable('user', [ |
||||
'id' => 'pk', |
||||
'login' => Schema::TYPE_STRING . ' NOT NULL', |
||||
'password' => Schema::TYPE_STRING . ' NOT NULL', |
||||
]); |
||||
} |
||||
|
||||
public function safeDown() |
||||
{ |
||||
$this->dropTable('news'); |
||||
$this->dropTable('user'); |
||||
} |
||||
|
||||
} |
||||
``` |
||||
|
||||
使用するクエリが一つだけでない場合は、`safeUp` と `safeDown` を使うことを推奨します。 |
||||
|
||||
> Note|注意: 全ての DBMS がトランザクションをサポートしている訳ではありません。 |
||||
> また、トランザクションに入れることが出来ない DB クエリもあります。 |
||||
> その場合には、代りに、`up()` と `down()` を実装しなければなりません。 |
||||
> また、MySQL の場合は、[暗黙のコミット](http://dev.mysql.com/doc/refman/5.1/en/implicit-commit.html) を引き起こす SQL 文があります。 |
||||
|
||||
|
||||
マイグレーションを適用する |
||||
-------------------------- |
||||
|
||||
利用できる全ての新しいマイグレーションを適用する (すなわち、ローカルのデータベースを最新の状態にする) ためには、次のコマンドを実行します。 |
||||
|
||||
``` |
||||
yii migrate |
||||
``` |
||||
|
||||
コマンドを実行すると、すべての新しいマイグレーションが一覧表示されます。 |
||||
マイグレーションを適用することを確認すると、クラス名のタイムスタンプの値の順に、一つずつ、すべての新しいマイグレーションクラスの `up()` メソッドが実行されます。 |
||||
|
||||
マイグレーションを適用した後に、マイグレーションツールは `migration` という名前のデータベーステーブルに記録を残します。 |
||||
これによって、ツールは、どのマイグレーションが適用済みで、どのマイグレーションが適用されていないかを特定することが出来ます。 |
||||
`migration` テーブルが存在しない場合は、ツールは `db` [アプリケーションコンポーネント](structure-application-components.md)によって指定されたデータベースの中にテーブルを自動的に作成します。 |
||||
|
||||
時として、新しいマイグレーションを一個また数個だけ適用したい場合があります。その場合は、次のコマンドを使うことが出来ます。 |
||||
|
||||
|
||||
``` |
||||
yii migrate/up 3 |
||||
``` |
||||
|
||||
このコマンドは、次の新しいマイグレーションを 3 個適用します。3 という値を変更して、適用されるマイグレーションの数を変更することが出来ます。 |
||||
|
||||
また、下記のコマンドを使って、特定のバージョンまでデータベースをマイグレートすることも可能です。 |
||||
|
||||
``` |
||||
yii migrate/to 101129_185401 |
||||
``` |
||||
|
||||
すなわち、マイグレーション名のタイムスタンプ部分を使って、データベースをマイグレートして到達したいバージョンを指定します。 |
||||
最後に適用されたマイグレーションと指定されたマイグレーションの間に複数のマイグレーションがある場合は、それらのマイグレーションがすべて適用されます。 |
||||
指定されたマイグレーションが適用済みである場合は、その後に適用されたすべてのマイグレーションが取り消されます (次の節で説明します)。 |
||||
|
||||
|
||||
マイグレーションを取り消す |
||||
-------------------------- |
||||
|
||||
適用された最後のマイグレーション (一個または複数個) を取り消したい場合は、下記のコマンドを使うことが出来ます。 |
||||
|
||||
|
||||
``` |
||||
yii migrate/down [step] |
||||
``` |
||||
|
||||
ここで、オプションの `step` パラメータは何個のマイグレーションを取り消すかを指定するものです。 |
||||
デフォルト値は 1 で、適用された最後のマイグレーションだけが取り消すされることを意味します。 |
||||
|
||||
前に説明したように、全てのマイグレーションが取り消せるとは限りません。 |
||||
取り消せないマイグレーションを取り消そうとすると例外が投げられて、取り消しのプロセス全体が終了させられます。 |
||||
|
||||
|
||||
マイグレーションを再適用する |
||||
---------------------------- |
||||
|
||||
マイグレーションの再適用とは、指定されたマイグレーションを最初に取り消してから、再度適用することを意味します。 |
||||
これは次のコマンドによって実行することが出来ます。 |
||||
|
||||
``` |
||||
yii migrate/redo [step] |
||||
``` |
||||
|
||||
ここで、オプションの `step` パラメータは何個のマイグレーションを再適用するかを指定するものです。 |
||||
デフォルト値は 1 で、最後のマイグレーションだけが再適用されることを意味します。 |
||||
|
||||
|
||||
マイグレーション情報を表示する |
||||
------------------------------ |
||||
|
||||
マイグレーションを適用したり取り消したりする他に、マイグレーションツールはマイグレーションの履歴、および、まだ適用されていない新しいマイグレーションを表示することも出来ます。 |
||||
|
||||
|
||||
``` |
||||
yii migrate/history [limit] |
||||
yii migrate/new [limit] |
||||
``` |
||||
|
||||
ここで、オプションの `limit` パラメータは、何個のマイグレーションを表示するかを指定するものです。 |
||||
`limit` が指定されない場合は、利用可能な全てのマイグレーションが表示されます。 |
||||
|
||||
最初のコマンドは適用済みのマイグレーションを表示し、第二のコマンドはまだ適用されていないマイグレーションを表示します。 |
||||
|
||||
|
||||
マイグレーション履歴を修正する |
||||
------------------------------ |
||||
|
||||
時として、実際には関係のあるマイグレーションを適用または取り消すことなく、マイグレーション履歴を特定のマイグレーションバージョンに修正したい場合があります。 |
||||
このことは新しいマイグレーションを開発するときにしばしば起ります。 |
||||
次のコマンドを使ってこの目的を達することが出来ます。 |
||||
|
||||
``` |
||||
yii migrate/mark 101129_185401 |
||||
``` |
||||
|
||||
このコマンドは `yii migrate/to` コマンドと非常によく似ていますが、マイグレーションを適用または取り消すことなく、マイグレーション履歴テーブルを指定されたバージョンに修正することだけを行うという点で違っています。 |
||||
|
||||
|
||||
マイグレーションコマンドをカスタマイズする |
||||
------------------------------------------ |
||||
|
||||
マイグレーションコマンドをカスタマイズする方法がいくつかあります。 |
||||
|
||||
### コマンドラインオプションを使う |
||||
|
||||
マイグレーションコマンドには、コマンドラインで指定できるいくつかのオプションがあります。 |
||||
|
||||
* `interactive`: 真偽値。マイグレーションを対話モードで実行するかどうかを指定します。 |
||||
デフォルト値は true で、指定されたマイグレーションを実行するときに、ユーザは何らかの入力を促されます。 |
||||
このオプションを false にセットして、マイグレーションがバックグラウンドプロセスとして実行されるようにすることが出来ます。 |
||||
|
||||
* `migrationPath`: 文字列。全てのマイグレーションクラスファイルを保存しているディレクトリを指定します。 |
||||
このパスは、パスエイリアスの形式で指定されなければならず、また、対応するディレクトリが存在する必要があります。 |
||||
このオプションが指定されない場合は、アプリケーションのベースパスの下の `migrations` サブディレクトリが使われます。 |
||||
|
||||
* `migrationTable`: 文字列。マイグレーション履歴の情報を保存するためのデータベーステーブル名を指定します。 |
||||
デフォルト値は `migration` です。このテーブルは、`version varchar(255) primary key, apply_time integer` という構造を持ちます。 |
||||
|
||||
* `db`: 文字列。データベース [アプリケーションコンポーネント](structure-application-components.md) の ID を指定します。 |
||||
デフォルト値は 'db' です。 |
||||
|
||||
* `templateFile`: 文字列。マイグレーションクラスを生成するためのコードテンプレートとして使われるファイルのパスを指定します。 |
||||
このパスは、パスエイリアスの形式で指定しなければなりません (例えば、`application.migrations.template`)。 |
||||
指定されない場合は、内部テンプレートが使用されます。 |
||||
テンプレートの中の `{ClassName}` というトークンが実際のマイグレーションクラス名によって置き換えられます。 |
||||
|
||||
これらのオプションを指定するためには、下記の書式を使って migrate コマンドを実行します。 |
||||
|
||||
``` |
||||
yii migrate/up --option1=value1 --option2=value2 ... |
||||
``` |
||||
|
||||
例えば、`forum` モジュールのためのマイグレーションを実行するときに、マイグレーションファイルがモジュールの `migrations` ディレクトリに置かれている場合には、下記のコマンドを使うことが出来ます。 |
||||
|
||||
|
||||
``` |
||||
yii migrate/up --migrationPath=@app/modules/forum/migrations |
||||
``` |
||||
|
||||
|
||||
### コマンドをグローバルに構成する |
||||
|
||||
コマンドラインオプションを使ってマイグレーションコマンドをその場その場で構成することも出来ますが、場合によっては、一度にまとめてコマンドを構成しておきたいこともあります。 |
||||
例えば、マイグレーションの履歴を保存するのに別のテーブルを使用したいとか、カスタマイズしたマイグレーションテンプレートを使用したいとかです。 |
||||
そうするためには、コンソールアプリケーションの構成情報ファイルを以下のように修正します。 |
||||
|
||||
|
||||
```php |
||||
'controllerMap' => [ |
||||
'migrate' => [ |
||||
'class' => 'yii\console\controllers\MigrateController', |
||||
'migrationTable' => 'my_custom_migrate_table', |
||||
], |
||||
] |
||||
``` |
||||
|
||||
これで、`migrate` コマンドを実行すると、上記の構成情報が効果を発揮して、毎回コマンドラインオプションを入力する必要がなくなります。 |
||||
その他のコマンドラインオプションも、このようにして構成することが出来ます。 |
||||
|
||||
|
||||
### 複数のデータベースのマイグレーション |
||||
|
||||
既定では、マイグレーションは `db` [アプリケーションコンポーネント](structure-application-components.md) によって指定されるデータベースに対して適用されます。 |
||||
これは、`--db` オプションを指定することによって変更することが出来ます。例えば、 |
||||
|
||||
``` |
||||
yii migrate --db=db2 |
||||
``` |
||||
|
||||
上記のコマンドは、既定のマイグレーションパスに置かれている *全ての* マイグレーションを `db2` データベースに適用するものです。 |
||||
|
||||
アプリケーションが複数のデータベースを扱っている場合は、いくつかのマイグレーションはあるデータベースに適用されなければならず、他のマイグレーションは別のデータベースに適用されなければならない、ということがあり得ます。 |
||||
そのような場合には、異なるデータベースごとに基底マイグレーションクラスを作成して、下記のように [[yii\db\Migration::init()]] メソッドをオーバーライドすることを推奨します。 |
||||
|
||||
```php |
||||
public function init() |
||||
{ |
||||
$this->db = 'db2'; |
||||
parent::init(); |
||||
} |
||||
``` |
||||
|
||||
こうすると、特定のデータベースに適用されるべきマイグレーションを作成するためには、対応する基底マイグレーションクラスから拡張するだけで済みます。 |
||||
これで、`yii migrate` コマンドを実行すると、全てのマイグレーションはそれぞれ対応するデータベースに対して適用されるようになります。 |
||||
|
||||
> Info|情報: それぞれのマイグレーションはハードコードされた DB 接続を使用しますので、`migrate` コマンドの `--db` オプションは効果を持ちません。 |
||||
また、マイグレーション履歴はデフォルトの `db` データベースに保存されることに注意してください。 |
||||
|
||||
`--db` オプションによる DB 接続の変更をサポートしたい場合は、次のような別の方法で複数のデータベースを扱うことが出来ます。 |
||||
|
||||
それぞれのデータベースについて、マイグレーションパスを作成し、対応する全てのマイグレーションクラスをそこに保存します。 |
||||
マイグレーションを適用するためには、次のようなコマンドを実行します。 |
||||
|
||||
``` |
||||
yii migrate --migrationPath=@app/migrations/db1 --db=db1 |
||||
yii migrate --migrationPath=@app/migrations/db2 --db=db2 |
||||
... |
||||
``` |
||||
|
||||
> Info|情報: 上記の方法では、マイグレーション履歴は `--db` オプションによって指定された別々のデータベースに保存されます。 |
@ -0,0 +1,6 @@
|
||||
Mongo DB |
||||
======== |
||||
|
||||
> Note|注意: この節はまだ執筆中です。 |
||||
> |
||||
> まだ内容がありません。 |
@ -0,0 +1,6 @@
|
||||
Redis |
||||
===== |
||||
|
||||
> Note|注意: この節はまだ執筆中です。 |
||||
> |
||||
> まだ内容がありません。 |
@ -0,0 +1,6 @@
|
||||
Sphinx Search |
||||
============= |
||||
|
||||
> Note|注意: この節はまだ執筆中です。 |
||||
> |
||||
> まだ内容がありません。 |
@ -0,0 +1,254 @@
|
||||
ファイルをアップロードする |
||||
========================== |
||||
|
||||
Yii におけるファイルのアップロードは、フォームモデル、その検証規則、そして、いくらかのコントローラコードによって行われます。 |
||||
アップロードを適切に処理するために何が必要とされるのか、見ていきましよう。 |
||||
|
||||
|
||||
一つのファイルをアップロードする |
||||
-------------------------------- |
||||
|
||||
まず最初に、ファイルのアップロードを処理するモデルを作成する必要があります。 |
||||
次の内容を持つ `models/UploadForm.php` を作って作成してください。 |
||||
|
||||
```php |
||||
namespace app\models; |
||||
|
||||
use yii\base\Model; |
||||
use yii\web\UploadedFile; |
||||
|
||||
/** |
||||
* UploadForm がアップロードのフォームの背後にあるモデルである。 |
||||
*/ |
||||
class UploadForm extends Model |
||||
{ |
||||
/** |
||||
* @var UploadedFile file 属性 |
||||
*/ |
||||
public $file; |
||||
|
||||
/** |
||||
* @return array 検証規則 |
||||
*/ |
||||
public function rules() |
||||
{ |
||||
return [ |
||||
[['file'], 'file'], |
||||
]; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
上記のコードにおいて作成した `UploadForm` というモデルは、HTML フォームで `<input type="file">` となる `$file` という属性を持ちます。 |
||||
この属性は [[yii\validators\FileValidator|FileValidator]] を使用する `file` という検証規則を持ちます。 |
||||
|
||||
### フォームのビュー |
||||
|
||||
次に、フォームを表示するビューを作成します。 |
||||
|
||||
```php |
||||
<?php |
||||
use yii\widgets\ActiveForm; |
||||
?> |
||||
|
||||
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?> |
||||
|
||||
<?= $form->field($model, 'file')->fileInput() ?> |
||||
|
||||
<button>送信</button> |
||||
|
||||
<?php ActiveForm::end() ?> |
||||
``` |
||||
|
||||
ファイルのアップロードを可能にする `'enctype' => 'multipart/form-data'` は不可欠です。 |
||||
`fileInput()` がフォームの入力フィールドを表します。 |
||||
|
||||
### コントローラ |
||||
|
||||
そして、フォームとモデルを結び付けるコントローラを作成します。 |
||||
|
||||
```php |
||||
namespace app\controllers; |
||||
|
||||
use Yii; |
||||
use yii\web\Controller; |
||||
use app\models\UploadForm; |
||||
use yii\web\UploadedFile; |
||||
|
||||
class SiteController extends Controller |
||||
{ |
||||
public function actionUpload() |
||||
{ |
||||
$model = new UploadForm(); |
||||
|
||||
if (Yii::$app->request->isPost) { |
||||
$model->file = UploadedFile::getInstance($model, 'file'); |
||||
|
||||
if ($model->file && $model->validate()) { |
||||
$model->file->saveAs('uploads/' . $model->file->baseName . '.' . $model->file->extension); |
||||
} |
||||
} |
||||
|
||||
return $this->render('upload', ['model' => $model]); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
`model->load(...)` の代りに `UploadedFile::getInstance(...)` を使っています。 |
||||
[[\yii\web\UploadedFile|UploadedFile]] はモデルの検証を実行せず、アップロードされたファイルに関する情報を提供するだけです。 |
||||
そのため、`$model->validate()` を手作業で実行して、[[yii\validators\FileValidator|FileValidator]] を起動する必要があります。 |
||||
[[yii\validators\FileValidator|FileValidator]] は、下記のコアコードが示しているように、属性がファイルであることを要求します。 |
||||
|
||||
```php |
||||
if (!$file instanceof UploadedFile || $file->error == UPLOAD_ERR_NO_FILE) { |
||||
return [$this->uploadRequired, []]; // "ファイルをアップロードしてください。" というエラーメッセージ |
||||
} |
||||
``` |
||||
|
||||
検証が成功したら、ファイルを保存します。 |
||||
|
||||
```php |
||||
$model->file->saveAs('uploads/' . $model->file->baseName . '.' . $model->file->extension); |
||||
``` |
||||
|
||||
「ベーシック」アプリケーションテンプレートを使っている場合は、`uploads` フォルダを `web` の下に作成しなければなりません。 |
||||
|
||||
以上です。ページをロードして、アップロードを試して見てください。ファイルは `basic/web/uploads` にアップロードされます。 |
||||
|
||||
検証 |
||||
---- |
||||
|
||||
たいていの場合、検証規則を調整して、特定のファイルだけを受け取るようにしたり、アップロードを必須としたりする必要があります。 |
||||
下記で、よく使われる規則の構成を見てみましよう。 |
||||
|
||||
### Required |
||||
|
||||
ファイルのアップロードを必須とする必要がある場合は、次のように `skipOnEmpty` を `false` に設定します。 |
||||
|
||||
```php |
||||
public function rules() |
||||
{ |
||||
return [ |
||||
[['file'], 'file', 'skipOnEmpty' => false], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
### MIME タイプ |
||||
|
||||
アップロードされるファイルのタイプを検証することは賢明なことです。 |
||||
`FileValidator` はこの目的のための `extensions` プロパティを持っています。 |
||||
|
||||
```php |
||||
public function rules() |
||||
{ |
||||
return [ |
||||
[['file'], 'file', 'extensions' => 'gif, jpg',], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
ただし、ファイル拡張子が検証されるだけで、実際のファイルの中身は検証されないことを憶えておいてください。 |
||||
ファイルの中身も検証するためには、`FileValidator` の `mimeType` プロパティを使います。 |
||||
|
||||
```php |
||||
public function rules() |
||||
{ |
||||
return [ |
||||
[['file'], 'file', 'extensions' => 'jpg, png', 'mimeTypes' => 'image/jpeg, image/png',], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
[一般的なメディアタイプの一覧表](http://en.wikipedia.org/wiki/Internet_media_type#List_of_common_media_types) |
||||
|
||||
### 画像のプロパティ |
||||
|
||||
画像をアップロードするときは、[[yii\validators\ImageValidator|ImageValidator]] が重宝するでしょう。 |
||||
このバリデータは、属性が有効な画像を受け取ったか否かを検証します。 |
||||
その画像は、[Imagine エクステンション](https://github.com/yiisoft/yii2/tree/master/extensions/imagine) によって、保存するか、または、処理することが出来ます。 |
||||
|
||||
複数のファイルをアップロードする |
||||
-------------------------------- |
||||
|
||||
複数のファイルを一度にアップロードする必要がある場合は、少し修正が必要になります。 |
||||
|
||||
モデル: |
||||
|
||||
```php |
||||
class UploadForm extends Model |
||||
{ |
||||
/** |
||||
* @var UploadedFile|Null ファイル属性 |
||||
*/ |
||||
public $file; |
||||
|
||||
/** |
||||
* @return array 検証規則 |
||||
*/ |
||||
public function rules() |
||||
{ |
||||
return [ |
||||
[['file'], 'file', 'maxFiles' => 10], // <--- ここ ! |
||||
]; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
ビュー: |
||||
|
||||
```php |
||||
<?php |
||||
use yii\widgets\ActiveForm; |
||||
|
||||
$form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]); |
||||
?> |
||||
|
||||
<?= $form->field($model, 'file[]')->fileInput(['multiple' => true]) ?> |
||||
|
||||
<button>送信</button> |
||||
|
||||
<?php ActiveForm::end(); ?> |
||||
``` |
||||
|
||||
違いがあるのは、次の行です。 |
||||
|
||||
```php |
||||
<?= $form->field($model, 'file[]')->fileInput(['multiple' => true]) ?> |
||||
``` |
||||
|
||||
コントローラ: |
||||
|
||||
```php |
||||
namespace app\controllers; |
||||
|
||||
use Yii; |
||||
use yii\web\Controller; |
||||
use app\models\UploadForm; |
||||
use yii\web\UploadedFile; |
||||
|
||||
class SiteController extends Controller |
||||
{ |
||||
public function actionUpload() |
||||
{ |
||||
$model = new UploadForm(); |
||||
|
||||
if (Yii::$app->request->isPost) { |
||||
$model->file = UploadedFile::getInstances($model, 'file'); |
||||
|
||||
if ($model->file && $model->validate()) { |
||||
foreach ($model->file as $file) { |
||||
$file->saveAs('uploads/' . $file->baseName . '.' . $file->extension); |
||||
} |
||||
} |
||||
} |
||||
|
||||
return $this->render('upload', ['model' => $model]); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
単一のファイルのアップロードとは、二つの点で異なります。 |
||||
最初の違いは、`UploadedFile::getInstance($model, 'file');` の代りに `UploadedFile::getInstances($model, 'file');` が使用されることです。 |
||||
前者が一つのインスタンスを返すだけなのに対して、後者はアップロードされた **全ての** ファイルのインスタンスを返します。 |
||||
第二の違いは、`foreach` によって、全てのファイルをそれぞれ保存している点です。 |
@ -0,0 +1,191 @@
|
||||
フォームを扱う |
||||
============== |
||||
|
||||
> Note|注意: この節はまだ執筆中です。 |
||||
|
||||
Yii においてフォームを使用する主たる方法は [[yii\widgets\ActiveForm]] によるものです。 |
||||
フォームがモデルに基づくものである場合はこの方法を優先すべきです。 |
||||
これに加えて、[[yii\helpers\Html]] にはいくつかの有用なメソッドがあり、通常は、あらゆるフォームにボタンやヘルプテキストを追加するのに使うことが出来ます。 |
||||
|
||||
モデルに基づくフォームを作成する場合、最初のステップは、モデルそのものを定義することです。 |
||||
モデルは、アクティブレコードクラス、あるいは、もっと汎用的な Model クラスから派生させることが出来ます。 |
||||
このログインフォームの例では、汎用的なモデルを使用します。 |
||||
|
||||
```php |
||||
use yii\base\Model; |
||||
|
||||
class LoginForm extends Model |
||||
{ |
||||
public $username; |
||||
public $password; |
||||
|
||||
/** |
||||
* @return array 検証規則 |
||||
*/ |
||||
public function rules() |
||||
{ |
||||
return [ |
||||
// username と password はともに必須 |
||||
[['username', 'password'], 'required'], |
||||
// password は validatePassword() によって検証される |
||||
['password', 'validatePassword'], |
||||
]; |
||||
} |
||||
|
||||
/** |
||||
* パスワードを検証する |
||||
* このメソッドがパスワードのインライン検証に使用される |
||||
*/ |
||||
public function validatePassword() |
||||
{ |
||||
$user = User::findByUsername($this->username); |
||||
if (!$user || !$user->validatePassword($this->password)) { |
||||
$this->addError('password', 'Incorrect username or password.'); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 提供された username と password でユーザをログインさせる。 |
||||
* @return boolean ユーザのログインが成功したかどうか |
||||
*/ |
||||
public function login() |
||||
{ |
||||
if ($this->validate()) { |
||||
$user = User::findByUsername($this->username); |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
``` |
||||
|
||||
コントローラはこのモデルのインスタンスをビューに渡し、ビューでは [[yii\widgets\ActiveForm|ActiveForm]] ウィジェットが使われます。 |
||||
|
||||
```php |
||||
use yii\helpers\Html; |
||||
use yii\widgets\ActiveForm; |
||||
|
||||
<?php $form = ActiveForm::begin([ |
||||
'id' => 'login-form', |
||||
'options' => ['class' => 'form-horizontal'], |
||||
]) ?> |
||||
<?= $form->field($model, 'username') ?> |
||||
<?= $form->field($model, 'password')->passwordInput() ?> |
||||
|
||||
<div class="form-group"> |
||||
<div class="col-lg-offset-1 col-lg-11"> |
||||
<?= Html::submitButton('ログイン', ['class' => 'btn btn-primary']) ?> |
||||
</div> |
||||
</div> |
||||
<?php ActiveForm::end() ?> |
||||
``` |
||||
|
||||
上記のコードでは、[[yii\widgets\ActiveForm::begin()|ActiveForm::begin()]] がフォームのインスタンスを作成するだけでなく、フォームの開始をマークしています。 |
||||
[[yii\widgets\ActiveForm::begin()|ActiveForm::begin()]] と [[yii\widgets\ActiveForm::end()|ActiveForm::end()]] の間に置かれた全てのコンテントが `<form>` タグによって囲まれます。 |
||||
その他のウィジェットと同じように、ウィジェットをどのように構成すべきかに関するオプションを指定するために、`begin` メソッドに配列を渡すことが出来ます。 |
||||
この例では、追加の CSS クラスと要素を特定するための ID が渡されて、開始 `<form>` タグに適用されています。 |
||||
|
||||
フォームの中で、フォームの要素を作成するために、ActiveForm ウィジェットの [[yii\widgets\ActiveForm::field()|ActiveForm::field()]] メソッドが呼ばれています。このメソッドは、要素のラベルと、適用できる JavaScript の検証メソッドがあれば、それらも追加します。 |
||||
このメソッドの呼び出し結果を直接にエコーすると、結果は通常の (text の) インプットになります。 |
||||
出力結果をカスタマイズするためには、このメソッドの呼び出しに追加のメソッドをチェーンします。 |
||||
|
||||
```php |
||||
<?= $form->field($model, 'password')->passwordInput() ?> |
||||
|
||||
// または |
||||
|
||||
<?= $form->field($model, 'username')->textInput()->hint('お名前を入力してください')->label('お名前') ?> |
||||
``` |
||||
|
||||
これで、フォームのフィールドによって定義されたテンプレートに従って、`<label>`、`<input>` など、全てのタグが生成されます。 |
||||
これらのタグを自分で追加するためには、`Html` ヘルパクラスを使うことが出来ます。 |
||||
|
||||
HTML5 のフィールドを使いたい場合は、次のように、インプットのタイプを直接に指定することが出来ます。 |
||||
|
||||
```php |
||||
<?= $form->field($model, 'email')->input('email') ?> |
||||
``` |
||||
|
||||
モデルの属性を指定するためには、もっと洗練された方法を使うことも出来ます。 |
||||
例えば、複数のファイルをアップロードしたり、複数の項目を選択したりする場合に、属性の名前に `[]` を付けて、属性が配列の値を取り得ることを指定することが出来ます。 |
||||
|
||||
```php |
||||
// 複数のファイルのアップロードを許可する |
||||
echo $form->field($model, 'uploadFile[]')->fileInput(['multiple'=>'multiple']); |
||||
|
||||
// 複数の項目をチェックすることを許可する |
||||
echo $form->field($model, 'items[]')->checkboxList(['a' => 'Item A', 'b' => 'Item B', 'c' => 'Item C']); |
||||
``` |
||||
|
||||
> **tip**|**ヒント**: 必須フィールドをアスタリスク付きのスタイルにするために、次の CSS を使うことが出来ます。 |
||||
> |
||||
```css |
||||
div.required label:after { |
||||
content: " *"; |
||||
color: red; |
||||
} |
||||
``` |
||||
|
||||
一つのフォームで複数のモデルを扱う |
||||
---------------------------------- |
||||
|
||||
時として、一つのフォームで同じ種類の複数のモデルを扱わなければならないことがあります。 |
||||
例えば、それぞれが「名前-値」の形で保存され、`Setting` モデルとして表される複数の設定項目を扱うフォームです。 |
||||
以下に、これを Yii で実装する方法を示します。 |
||||
|
||||
コントローラアクションから始めましよう。 |
||||
|
||||
```php |
||||
namespace app\controllers; |
||||
|
||||
use Yii; |
||||
use yii\base\Model; |
||||
use yii\web\Controller; |
||||
use app\models\Setting; |
||||
|
||||
class SettingsController extends Controller |
||||
{ |
||||
// ... |
||||
|
||||
public function actionUpdate() |
||||
{ |
||||
$settings = Setting::find()->indexBy('id')->all(); |
||||
|
||||
if (Model::loadMultiple($settings, Yii::$app->request->post()) && Model::validateMultiple($settings)) { |
||||
foreach ($settings as $setting) { |
||||
$setting->save(false); |
||||
} |
||||
|
||||
return $this->redirect('index'); |
||||
} |
||||
|
||||
return $this->render('update', ['settings' => $settings]); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
上記のコードでは、データベースからモデルを読み出すときに `indexBy` を使って、モデルの ID でインデックスされた配列にモデルを代入しています。 |
||||
このインデックスが、後で、フォームフィールドを特定するために使われます。 |
||||
`loadMultiple` が POST から来るフォームデータを複数のモデルに代入し、`validateMultiple` が全てのモデルを一度に検証します。 |
||||
保存するときの検証をスキップするために、`save` のパラメータに `false` を渡しています。 |
||||
|
||||
次に、`update` ビューにあるフォームです。 |
||||
|
||||
```php |
||||
<?php |
||||
use yii\helpers\Html; |
||||
use yii\widgets\ActiveForm; |
||||
|
||||
$form = ActiveForm::begin(); |
||||
|
||||
foreach ($settings as $index => $setting) { |
||||
echo Html::encode($setting->name) . ': ' . $form->field($setting, "[$index]value"); |
||||
} |
||||
|
||||
ActiveForm::end(); |
||||
``` |
||||
|
||||
ここで全ての設定項目について、それぞれ、名前と値を入れたインプットをレンダリングしています。 |
||||
インプットの名前に適切なインデックスを追加することが肝腎です。 |
||||
というのは、`loadMultiple` がそれを見て、どのモデルにどの値を代入するかを決定するからです。 |
@ -0,0 +1,6 @@
|
||||
複数のモデルを扱う複雑なフォーム |
||||
================================ |
||||
|
||||
> Note|注意: この節はまだ執筆中です。 |
||||
> |
||||
> まだ内容がありません。 |
@ -0,0 +1,610 @@
|
||||
入力を検証する |
||||
============== |
||||
|
||||
大まかに言うなら、エンドユーザから受信したデータは決して信用せず、利用する前に検証しなければならない、ということです。 |
||||
|
||||
[モデル](structure-models.md) にユーザの入力が投入されたら、モデルの [[yii\base\Model::validate()]] メソッドを呼んで入力を検証することが出来ます。 |
||||
このメソッドはバリデーションが成功したか否かを示す真偽値を返します。 |
||||
バリデーションが失敗した場合は、[[yii\base\Model::errors]] プロパティからエラーメッセージを取得することが出来ます。 |
||||
例えば、 |
||||
|
||||
```php |
||||
$model = new \app\models\ContactForm; |
||||
|
||||
// モデルの属性にユーザ入力を投入する |
||||
$model->attributes = \Yii::$app->request->post('ContactForm'); |
||||
|
||||
if ($model->validate()) { |
||||
// 全ての入力が有効 |
||||
} else { |
||||
// バリデーションが失敗。$errors はエラーメッセージを含む配列 |
||||
$errors = $model->errors; |
||||
} |
||||
``` |
||||
|
||||
## 規則を宣言する <a name="declaring-rules"></a> |
||||
|
||||
`validate()` を現実に動作させるためには、検証する予定の属性に対してバリデーション規則を宣言しなければなりません。 |
||||
規則は [[yii\base\Model::rules()]] メソッドをオーバーライドすることで宣言します。 |
||||
次の例は、`ContactForm` モデルに対してバリデーション規則を宣言する方法を示すものです。 |
||||
|
||||
```php |
||||
public function rules() |
||||
{ |
||||
return [ |
||||
// 名前、メールアドレス、主題、本文が必須項目 |
||||
[['name', 'email', 'subject', 'body'], 'required'], |
||||
|
||||
// email 属性は有効なメールアドレスでなければならない |
||||
['email', 'email'], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
[[yii\base\Model::rules()|rules()]] メソッドは配列を返すべきものですが、配列の各要素は次の形式の配列でなければなりません。 |
||||
|
||||
```php |
||||
[ |
||||
// 必須。この規則によって検証されるべき属性を指定する。 |
||||
// 属性が一つだけの場合は、配列の中に入れずに、属性の名前を直接に書いてもよい。 |
||||
['属性1', '属性2', ...], |
||||
|
||||
// 必須。この規則のタイプを指定する。 |
||||
// クラス名、バリデータのエイリアス、または、バリデーションメソッドの名前。 |
||||
'バリデータ', |
||||
|
||||
// オプション。この規則が適用されるべき一つまたは複数のシナリオを指定する。 |
||||
// 指定しない場合は、この規則が全てのシナリオに適用されることを意味する。 |
||||
// "except" オプションを構成して、列挙したシナリオを除く全てのシナリオに |
||||
// この規則が適用されるべきことを指定することも出来る。 |
||||
'on' => ['シナリオ1', 'シナリオ2', ...], |
||||
|
||||
// オプション。バリデータオブジェクトに対する追加の構成情報を指定する。 |
||||
'プロパティ1' => '値1', 'プロパティ2' => '値2', ... |
||||
] |
||||
``` |
||||
|
||||
各規則について、最低限、規則がどの属性に適用されるか、そして、規則がどのタイプであるかを指定しなければなりません。 |
||||
規則のタイプは、次に挙げる形式のどれか一つを選ぶことが出来ます。 |
||||
|
||||
* コアバリデータのエイリアス。例えば、`required`、`in`、`date`、等々。 |
||||
コアバリデータの完全なリストは [コアバリデータ](tutorial-core-validators.md) を参照してください。 |
||||
* モデルクラス内のバリデーションメソッドの名前、または無名関数。詳細は、[インラインバリデータ](#inline-validators) の項を参照してください。 |
||||
* 完全修飾のバリデータクラス名。詳細は [スタンドアロンバリデータ](#standalone-validators) の項を参照してください。 |
||||
|
||||
一つの規則は、一つまたは複数の属性を検証するために使用することが出来ます。 |
||||
そして、一つの属性は、一つまたは複数の規則によって検証され得ます。 |
||||
`on` オプションを指定することで、規則を特定の [シナリオ](structure-models.md#scenarios) においてのみ適用することが出来ます。 |
||||
`on` オプションを指定しない場合は、規則が全てのシナリオに適用されることになります。 |
||||
|
||||
`validate()` メソッドが呼ばれると、次のステップを踏んでバリデーションが実行されます。 |
||||
|
||||
1. 現在の [[yii\base\Model::scenario|シナリオ]] を使って [[yii\base\Model::scenarios()]] から属性のリストを取得し、どの属性が検証されるべきかを決定します。 |
||||
検証されるべき属性が *アクティブな属性* と呼ばれます。 |
||||
2. 現在の [[yii\base\Model::scenario|シナリオ]] を使って [[yii\base\Model::rules()]] から規則のリストを取得し、どのバリデーション規則が使用されるべきかを決定します。 |
||||
使用されるべき規則が *アクティブな規則* と呼ばれます。 |
||||
3. 全てのアクティブな規則を一つずつ使って、その規則に関連付けられた全てのアクティブな属性を一つずつ検証します。 |
||||
バリデーション規則はリストに挙げられている順に評価されます。 |
||||
|
||||
属性は、上記のバリデーションのステップに従って、`scenarios()` でアクティブな属性であると宣言されており、かつ、`rules()` で宣言された一つまたは複数のアクティブな規則と関連付けられている場合に、また、その場合に限って、検証されます。 |
||||
|
||||
|
||||
### エラーメッセージをカスタマイズする <a name="customizing-error-messages"></a> |
||||
|
||||
たいていのバリデータはデフォルトのエラーメッセージを持っていて、属性のバリデーションが失敗した場合にそれを検証の対象であるモデルに追加します。 |
||||
例えば、[[yii\validators\RequiredValidator|required]] バリデータは、このバリデータを使って `username` 属性を検証したとき、規則に合致しない場合は「ユーザ名は空ではいけません。」というエラーメッセージをモデルに追加します。 |
||||
|
||||
規則のエラーメッセージは、次に示すように、規則を宣言するときに `message` プロパティを指定することによってカスタマイズすることが出来ます。 |
||||
|
||||
```php |
||||
public function rules() |
||||
{ |
||||
return [ |
||||
['username', 'required', 'message' => 'ユーザ名を選んでください。'], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
バリデータの中には、バリデーションを失敗させたさまざまな原因をより詳しく説明するための追加のエラーメッセージをサポートしているものがあります。 |
||||
例えば、[[yii\validators\NumberValidator|number]] バリデータは、検証される値が大きすぎたり小さすぎたりしたときにバリデーションの失敗を説明するために、それぞれ、[[yii\validators\NumberValidator::tooBig|tooBig]] および [[yii\validators\NumberValidator::tooSmall|tooSmall]] のメッセージをサポートしています。 |
||||
これらのエラーメッセージも、バリデータの他のプロパティと同様、バリデーション規則の中で構成することが出来ます。 |
||||
|
||||
|
||||
### バリデーションのイベント <a name="validation-events"></a> |
||||
|
||||
[[yii\base\Model::validate()]] は、呼び出されると、バリデーションプロセスをカスタマイズするためにオーバーライドできる二つのメソッドを呼び出します。 |
||||
|
||||
* [[yii\base\Model::beforeValidate()]]: 既定の実装は [[yii\base\Model::EVENT_BEFORE_VALIDATE]] イベントをトリガするものです。 |
||||
このメソッドをオーバーライドするか、または、イベントに反応して、バリデーションが実行される前に、何らかの前処理 (例えば入力されたデータの正規化) をすることが出来ます。 |
||||
このメソッドは、バリデーションを続行すべきか否かを示す真偽値を返さなくてはなりません。 |
||||
* [[yii\base\Model::afterValidate()]]: 既定の実装は [[yii\base\Model::EVENT_AFTER_VALIDATE]] イベントをトリガするものです。 |
||||
このメソッドをオーバーライドするか、または、イベントに反応して、バリデーションが完了した後に、何らかの後処理をすることが出来ます。 |
||||
|
||||
|
||||
### 条件付きバリデーション <a name="conditional-validation"></a> |
||||
|
||||
特定の条件が満たされる場合に限って属性を検証したい場合、例えば、ある属性のバリデーションが他の属性の値に依存する場合には、[[yii\validators\Validator::when|when]] プロパティを使って、そのような条件を定義することが出来ます。 |
||||
例えば、 |
||||
|
||||
```php |
||||
[ |
||||
['state', 'required', 'when' => function($model) { |
||||
return $model->country == 'USA'; |
||||
}], |
||||
] |
||||
``` |
||||
|
||||
[[yii\validators\Validator::when|when]] プロパティは、次のシグニチャを持つ PHP コーラブルを値として取ります。 |
||||
|
||||
```php |
||||
/** |
||||
* @param Model $model 検証されるモデル |
||||
* @param string $attribute 検証される属性 |
||||
* @return boolean 規則が適用されるか否か |
||||
*/ |
||||
function ($model, $attribute) |
||||
``` |
||||
|
||||
クライアント側でも条件付きバリデーションをサポートする必要がある場合は、[[yii\validators\Validator::whenClient|whenClient]] プロパティを構成しなければなりません。 |
||||
このプロパティは、規則を適用すべきか否かを返す JavaScript 関数を表す文字列を値として取ります。 |
||||
例えば、 |
||||
|
||||
```php |
||||
[ |
||||
['state', 'required', 'when' => function ($model) { |
||||
return $model->country == 'USA'; |
||||
}, 'whenClient' => "function (attribute, value) { |
||||
return $('#country').val() == 'USA'; |
||||
}"], |
||||
] |
||||
``` |
||||
|
||||
|
||||
### データのフィルタリング <a name="data-filtering"></a> |
||||
|
||||
ユーザ入力をフィルタまたは前処理する必要があることがよくあります。 |
||||
例えば、`username` の入力値の前後にある空白を除去したいというような場合です。 |
||||
この目的を達するためにバリデーション規則を使うことが出来ます。 |
||||
|
||||
次の例では、入力値の前後にある空白を除去して、空の入力値を null に変換することを、[trim](tutorial-core-validators.md#trim) および [default](tutorial-core-validators.md#default) のコアバリデータで行っています。 |
||||
|
||||
```php |
||||
[ |
||||
[['username', 'email'], 'trim'], |
||||
[['username', 'email'], 'default'], |
||||
] |
||||
``` |
||||
|
||||
もっと汎用的な [filter](tutorial-core-validators.md#filter) バリデータを使って、もっと複雑なデータフィルタリングをすることも出来ます。 |
||||
お分かりかと思いますが、これらのバリデーション規則は実際には入力を検証しません。そうではなくて、検証される属性の値を処理して書き戻すのです。 |
||||
|
||||
|
||||
### 空の入力値を扱う <a name="handling-empty-inputs"></a> |
||||
|
||||
HTML フォームから入力データが送信されたとき、入力値が空である場合には何らかのデフォルト値を割り当てなければならないことがよくあります。 |
||||
[default](tutorial-core-validators.md#default) バリデータを使ってそうすることが出来ます。 |
||||
例えば、 |
||||
|
||||
```php |
||||
[ |
||||
// 空の時は "username" と "email" を null にする |
||||
[['username', 'email'], 'default'], |
||||
|
||||
// 空の時は "level" を 1 にする |
||||
['level', 'default', 'value' => 1], |
||||
] |
||||
``` |
||||
|
||||
既定では、入力値が空であると見なされるのは、それが、空文字列であるか、空配列であるか、null であるときです。 |
||||
空を検知するこのデフォルトのロジックは、[[yii\validators\Validator::isEmpty]] プロパティを PHP コーラブルで構成することによって、カスタマイズすることが出来ます。 |
||||
例えば、 |
||||
|
||||
```php |
||||
[ |
||||
['agree', 'required', 'isEmpty' => function ($value) { |
||||
return empty($value); |
||||
}], |
||||
] |
||||
``` |
||||
|
||||
> Note|注意: たいていのバリデータは、[[yii\base\Validator::skipOnEmpty]] プロパティがデフォルト値 `true` を取っている場合は、空の入力値を処理しません。 |
||||
そのようなバリデータは、関連付けられた属性が空の入力値を受け取ったときは、バリデーションの過程ではスキップされるだけになります。 |
||||
[コアバリデータ](tutorial-core-validators.md) の中では、`captcha`、`default`、`filter`、`required`、そして `trim` だけが空の入力値を処理します。 |
||||
|
||||
|
||||
## アドホックなバリデーション <a name="ad-hoc-validation"></a> |
||||
|
||||
時として、何らかのモデルに結び付けられていない値に対する *アドホックなバリデーション* を実行しなければならない場合があります。 |
||||
|
||||
実行する必要があるバリデーションが一種類 (例えば、メールアドレスの検証) だけである場合は、使いたいバリデータの [[yii\validators\Validator::validate()|validate()]] メソッドを次のように呼び出すことが出来ます。 |
||||
|
||||
```php |
||||
$email = 'test@example.com'; |
||||
$validator = new yii\validators\EmailValidator(); |
||||
|
||||
if ($validator->validate($email, $error)) { |
||||
echo 'メールアドレスは有効。'; |
||||
} else { |
||||
echo $error; |
||||
} |
||||
``` |
||||
|
||||
> Note|注意: 全てのバリデータがこの種のバリデーションをサポートしている訳ではありません。 |
||||
その一例が [unique](tutorial-core-validators.md#unique) コアバリデータであり、これはモデルとともに使用されることだけを念頭にして設計されています。 |
||||
|
||||
いくつかの値に対して複数のバリデーションを実行する必要がある場合は、属性と規則の両方をその場で宣言することが出来る [[yii\base\DynamicModel]] を使うことが出来ます。 |
||||
これは、次のような使い方をします。 |
||||
|
||||
```php |
||||
public function actionSearch($name, $email) |
||||
{ |
||||
$model = DynamicModel::validateData(compact('name', 'email'), [ |
||||
[['name', 'email'], 'string', 'max' => 128], |
||||
['email', 'email'], |
||||
]); |
||||
|
||||
if ($model->hasErrors()) { |
||||
// バリデーションが失敗 |
||||
} else { |
||||
// バリデーションが成功 |
||||
} |
||||
} |
||||
``` |
||||
|
||||
[[yii\base\DynamicModel::validateData()]] メソッドは `DynamicModel` のインスタンスを作成し、与えられた値 (この例では `name` と `email`) を使って属性を定義し、そして、与えられた規則で [[yii\base\Model::validate()]] を呼び出します。 |
||||
|
||||
別の選択肢として、次のように、もっと「クラシック」な構文を使って、アドホックなデータバリデーションを実行することも出来ます。 |
||||
|
||||
```php |
||||
public function actionSearch($name, $email) |
||||
{ |
||||
$model = new DynamicModel(compact('name', 'email')); |
||||
$model->addRule(['name', 'email'], 'string', ['max' => 128]) |
||||
->addRule('email', 'email') |
||||
->validate(); |
||||
|
||||
if ($model->hasErrors()) { |
||||
// バリデーションが失敗 |
||||
} else { |
||||
// バリデーションが成功 |
||||
} |
||||
} |
||||
``` |
||||
|
||||
検証を実行した後は、通常のモデルで行うのと同様に、バリデーションが成功したか否かを [[yii\base\DynamicModel::hasErrors()|hasErrors()]] メソッドを呼んでチェックして、[[yii\base\DynamicModel::errors|errors]] プロパティからバリデーションエラーを取得することが出来ます。 |
||||
また、このモデルのインスタンスによって定義された動的な属性に対しても、例えば `$model->name` や `$model->email` のようにして、アクセスすることが出来ます。 |
||||
|
||||
|
||||
## バリデータを作成する <a name="creating-validators"></a> |
||||
|
||||
Yii のリリースに含まれている [コアバリデータ](tutorial-core-validators.md) を使う以外に、あなた自身のバリデータを作成することも出来ます。 |
||||
インラインバリデータとスタンドアロンバリデータを作ることが出来ます。 |
||||
|
||||
|
||||
### インラインバリデータ <a name="inline-validators"></a> |
||||
|
||||
インラインバリデータは、モデルのメソッドまたは無名関数として定義されるバリデータです。 |
||||
メソッド/関数 のシグニチャは、 |
||||
|
||||
```php |
||||
/** |
||||
* @param string $attribute 現在検証されている属性 |
||||
* @param array $params 規則に与えられる追加の「名前-値」のペア |
||||
*/ |
||||
function ($attribute, $params) |
||||
``` |
||||
|
||||
属性がバリデーションに失敗した場合は、メソッド/関数 は [[yii\base\Model::addError()]] を呼んでエラーメッセージをモデルに保存し、後で読み出してエンドユーザに示ことが出来るようにしなければなりません。 |
||||
|
||||
下記にいくつかの例を示します。 |
||||
|
||||
```php |
||||
use yii\base\Model; |
||||
|
||||
class MyForm extends Model |
||||
{ |
||||
public $country; |
||||
public $token; |
||||
|
||||
public function rules() |
||||
{ |
||||
return [ |
||||
// モデルメソッド validateCountry() として定義されるインラインバリデータ |
||||
['country', 'validateCountry'], |
||||
|
||||
// 無名関数として定義されるインラインバリデータ |
||||
['token', function ($attribute, $params) { |
||||
if (!ctype_alnum($this->$attribute)) { |
||||
$this->addError($attribute, 'トークンは英数字で構成しなければなりません。'); |
||||
} |
||||
}], |
||||
]; |
||||
} |
||||
|
||||
public function validateCountry($attribute, $params) |
||||
{ |
||||
if (!in_array($this->$attribute, ['USA', 'Web'])) { |
||||
$this->addError($attribute, '国は "USA" または "Web" でなければなりません。'); |
||||
} |
||||
} |
||||
} |
||||
``` |
||||
|
||||
> Note|注意: 既定では、インラインバリデータは、関連付けられている属性が空の入力値を受け取ったり、既に何らかのバリデーション規則に失敗したりしている場合には、適用されません。 |
||||
> 規則が常に適用されることを保証したい場合は、規則の宣言において [[yii\validators\Validator::skipOnEmpty|skipOnEmpty]] および/または [[yii\validators\Validator::skipOnError|skipOnError]] のプロパティを false に設定することが出来ます。 |
||||
> 例えば、 |
||||
> |
||||
> ```php |
||||
> [ |
||||
> ['country', 'validateCountry', 'skipOnEmpty' => false, 'skipOnError' => false], |
||||
> ] |
||||
> ``` |
||||
|
||||
|
||||
### スタンドアロンバリデータ <a name="standalone-validators"></a> |
||||
|
||||
スタンドアロンバリデータは、[[yii\validators\Validator]] またはその子クラスを拡張するクラスです。 |
||||
[[yii\validators\Validator::validateAttribute()]] メソッドをオーバーライドすることによって、その検証ロジックを実装することが出来ます。 |
||||
[インラインバリデータ](#inline-validators) でするのと同じように、属性がバリデーションに失敗した場合は、[[yii\base\Model::addError()]] を呼んでエラーメッセージをモデルに保存します。 |
||||
例えば、 |
||||
|
||||
```php |
||||
namespace app\components; |
||||
|
||||
use yii\validators\Validator; |
||||
|
||||
class CountryValidator extends Validator |
||||
{ |
||||
public function validateAttribute($model, $attribute) |
||||
{ |
||||
if (!in_array($model->$attribute, ['USA', 'Web'])) { |
||||
$this->addError($model, $attribute, '国は "USA" または "Web" でなければなりません。'); |
||||
} |
||||
} |
||||
} |
||||
``` |
||||
|
||||
|
||||
あなたのバリデータで、モデル無しの値のバリデーションをサポートしたい場合は、[[yii\validators\Validator::validate()]] もオーバーライドしなければなりません。 |
||||
または、`validateAttribute()` と `validate()` の代りに、[[yii\validators\Validator::validateValue()]] をオーバーライドしても構いません。 |
||||
と言うのは、前の二つは、デフォルトでは、`validateValue()` を呼び出すことによって実装されているからです。 |
||||
|
||||
|
||||
## クライアント側でのバリデーション <a name="client-side-validation"></a> |
||||
|
||||
エンドユーザが HTML フォームで値を入力する際には、JavaScript に基づくクライアント側でのバリデーションを提供することが望まれます。 |
||||
というのは、クライアント側でのバリデーションは、ユーザが入力のエラーを早く見つけることが出来るようにすることによって、より良いユーザ体験を提供するものだからです。 |
||||
あなたも、サーバ側でのバリデーション *に加えて* クライアント側でのバリデーションをサポートするバリデータを使用したり実装したりすることが出来ます。 |
||||
|
||||
> Info|情報: クライアント側でのバリデーションは望ましいものですが、不可欠なものではありません。 |
||||
その主たる目的は、ユーザにより良い体験を提供することにあります。 |
||||
エンドユーザから来る入力値と同じように、クライアント側でのバリデーションを決して信用してはいけません。 |
||||
この理由により、これまでの項で説明したように、常に [[yii\base\Model::validate()]] を呼び出してサーバ側でのバリデーションを実行しなければなりません。 |
||||
|
||||
|
||||
### クライアント側でのバリデーションを使う <a name="using-client-side-validation"></a> |
||||
|
||||
多くの [コアバリデータ](tutorial-core-validators.md) は、そのままで、クライアント側でのバリデーションをサポートしています。 |
||||
あなたがする必要があるのは、[[yii\widgets\ActiveForm]] を使って HTML フォームを作るということだけです。 |
||||
例えば、下の `LoginForm` は二つの規則を宣言しています。 |
||||
一つは、[required](tutorial-core-validators.md#required) コアバリデータを使っていますが、これはクライアント側とサーバ側の両方でサポートされています。 |
||||
もう一つは `validatePassword` インラインバリデータを使っていますが、こちらはサーバ側でのみサポートされています。 |
||||
|
||||
```php |
||||
namespace app\models; |
||||
|
||||
use yii\base\Model; |
||||
use app\models\User; |
||||
|
||||
class LoginForm extends Model |
||||
{ |
||||
public $username; |
||||
public $password; |
||||
|
||||
public function rules() |
||||
{ |
||||
return [ |
||||
// username と password はともに必須 |
||||
[['username', 'password'], 'required'], |
||||
|
||||
// password は validatePassword() によって検証される |
||||
['password', 'validatePassword'], |
||||
]; |
||||
} |
||||
|
||||
public function validatePassword() |
||||
{ |
||||
$user = User::findByUsername($this->username); |
||||
|
||||
if (!$user || !$user->validatePassword($this->password)) { |
||||
$this->addError('password', 'ユーザ名またはパスワードが違います。'); |
||||
} |
||||
} |
||||
} |
||||
``` |
||||
|
||||
次のコードによって構築される HTML フォームは、`username` と `password` の二つの入力フィールドを含みます。 |
||||
何も入力せずにこのフォームを送信すると、何かを入力するように要求するエラーメッセージが、サーバと少しも交信することなく、ただちに表示されることに気付くでしょう。 |
||||
|
||||
```php |
||||
<?php $form = yii\widgets\ActiveForm::begin(); ?> |
||||
<?= $form->field($model, 'username') ?> |
||||
<?= $form->field($model, 'password')->passwordInput() ?> |
||||
<?= Html::submitButton('ログイン') ?> |
||||
<?php yii\widgets\ActiveForm::end(); ?> |
||||
``` |
||||
|
||||
舞台裏では、[[yii\widgets\ActiveForm]] がモデルで宣言されているバリデーション規則を読んで、クライアント側のバリデーションをサポートするバリデータのために適切な JavaScript コードを生成します。 |
||||
ユーザが入力フィールドの値を変更したりフォームを送信したりすると、クライアント側バリデーションの JavaScript が起動されます。 |
||||
|
||||
クライアント側のバリデーションを完全に無効にしたい場合は、[[yii\widgets\ActiveForm::enableClientValidation]] プロパティを false に設定することが出来ます。 |
||||
また、個々の入力フィールドごとにクライアント側のバリデーションを無効にしたい場合は、入力フィールドの [[yii\widgets\ActiveField::enableClientValidation]] プロパティを false に設定することも出来ます。 |
||||
|
||||
|
||||
### クライアント側バリデーションを実装する <a name="implementing-client-side-validation"></a> |
||||
|
||||
クライアント側バリデーションをサポートするバリデータを作成するためには、クライアント側でのバリデーションを実行する JavaScript コードを返す [[yii\validators\Validator::clientValidateAttribute()]] メソッドを実装しなければなりません。 |
||||
その JavaScript の中では、次の事前定義された変数を使用することが出来ます。 |
||||
|
||||
- `attribute`: 検証される属性の名前。 |
||||
- `value`: 検証される値。 |
||||
- `messages`: 属性に対する検証のエラーメッセージを保持するために使用される配列。 |
||||
- `deferred`: Deferred オブジェクトをプッシュして入れることが出来る配列 (次の項で説明します)。 |
||||
|
||||
次の例では、既存のステータスのデータに含まれる有効な値が入力されたかどうかを検証する `StatusValidator` を作成します。 |
||||
このバリデータは、サーバ側とクライアント側の両方のバリデーションをサポートします。 |
||||
|
||||
```php |
||||
namespace app\components; |
||||
|
||||
use yii\validators\Validator; |
||||
use app\models\Status; |
||||
|
||||
class StatusValidator extends Validator |
||||
{ |
||||
public function init() |
||||
{ |
||||
parent::init(); |
||||
$this->message = '無効なステータスが入力されました。'; |
||||
} |
||||
|
||||
public function validateAttribute($model, $attribute) |
||||
{ |
||||
$value = $model->$attribute; |
||||
if (!Status::find()->where(['id' => $value])->exists()) { |
||||
$model->addError($attribute, $this->message); |
||||
} |
||||
} |
||||
|
||||
public function clientValidateAttribute($model, $attribute, $view) |
||||
{ |
||||
$statuses = json_encode(Status::find()->select('id')->asArray()->column()); |
||||
$message = json_encode($this->message, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); |
||||
return <<<JS |
||||
if (!$.inArray(value, $statuses)) { |
||||
messages.push($message); |
||||
} |
||||
JS; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
> Tip|ヒント: 上記のコード例の主たる目的は、クライアント側バリデーションをサポートする方法を説明することにあります。 |
||||
> 実際の仕事では、[in](tutorial-core-validators.md#in) コアバリデータを使って、同じ目的を達することが出来ます。 |
||||
> 次のようにバリデーション規則を書けばよいのです。 |
||||
> |
||||
> ```php |
||||
> [ |
||||
> ['status', 'in', 'range' => Status::find()->select('id')->asArray()->column()], |
||||
> ] |
||||
> ``` |
||||
|
||||
### Deferred バリデーション <a name="deferred-validation"></a> |
||||
|
||||
非同期のクライアント側バリデーションをサポートする必要がある場合は、[Defered オブジェクト](http://api.jquery.com/category/deferred-object/) を作成することが出来ます。 |
||||
例えば、AJAX によるカスタムバリデーションを実行するために、次のコードを使うことが出来ます。 |
||||
|
||||
```php |
||||
public function clientValidateAttribute($model, $attribute, $view) |
||||
{ |
||||
return <<<JS |
||||
deferred.push($.get("/check", {value: value}).done(function(data) { |
||||
if ('' !== data) { |
||||
messages.push(data); |
||||
} |
||||
})); |
||||
JS; |
||||
} |
||||
``` |
||||
|
||||
上のコードにおいて、`deferred` は Yii が提供する変数で、Deferred オブジェクトの配列です。 |
||||
jQuery の `$.get()` メソッドによって作成された Deferred オブジェクトが `deferred` 配列にプッシュされています。 |
||||
|
||||
Deferred オブジェクトを明示的に作成して、非同期のコールバックが呼ばれたときに、Deferred オブジェクトの `resolve()` メソッドを呼ぶことも出来ます。 |
||||
次の例は、アップロードされる画像ファイルの大きさをクライアント側で検証する方法を示すものです。 |
||||
|
||||
```php |
||||
public function clientValidateAttribute($model, $attribute, $view) |
||||
{ |
||||
return <<<JS |
||||
var def = $.Deferred(); |
||||
var img = new Image(); |
||||
img.onload = function() { |
||||
if (this.width > 150) { |
||||
messages.push('画像の幅が大きすぎます。'); |
||||
} |
||||
def.resolve(); |
||||
} |
||||
var reader = new FileReader(); |
||||
reader.onloadend = function() { |
||||
img.src = reader.result; |
||||
} |
||||
reader.readAsDataURL(file); |
||||
|
||||
deferred.push(def); |
||||
JS; |
||||
} |
||||
``` |
||||
|
||||
> Note|注意: 属性が検証された後に、`resolve()` メソッドを呼び出さなければなりません。 |
||||
そうしないと、主たるフォームのバリデーションが完了しません。 |
||||
|
||||
簡潔に記述できるように、`deferred` 配列はショートカットメソッド `add()` を装備しており、このメソッドを使うと、自動的に Deferred オブジェクトを作成して `deferred` 配列に追加することが出来ます。 |
||||
このメソッドを使えば、上記の例は次のように簡潔に記すことが出来ます。 |
||||
|
||||
```php |
||||
public function clientValidateAttribute($model, $attribute, $view) |
||||
{ |
||||
return <<<JS |
||||
deferred.add(function(def) { |
||||
var img = new Image(); |
||||
img.onload = function() { |
||||
if (this.width > 150) { |
||||
messages.push('画像の幅が大きすぎます。'); |
||||
} |
||||
def.resolve(); |
||||
} |
||||
var reader = new FileReader(); |
||||
reader.onloadend = function() { |
||||
img.src = reader.result; |
||||
} |
||||
reader.readAsDataURL(file); |
||||
}); |
||||
JS; |
||||
} |
||||
``` |
||||
|
||||
|
||||
### AJAX バリデーション <a name="ajax-validation"></a> |
||||
|
||||
場合によっては、サーバだけが必要な情報を持っているために、サーバ側でしか検証が実行できないことがあります。 |
||||
例えば、ユーザ名がユニークであるか否かを検証するためには、サーバ側で user テーブルを調べることが必要になります。 |
||||
このような場合には、AJAX ベースのバリデーションを使うことが出来ます。 |
||||
AJAX バリデーションは、通常のクライアント側でのバリデーションと同じユーザ体験を保ちながら、入力値を検証するためにバックグラウンドで AJAX リクエストを発行します。 |
||||
|
||||
AJAX バリデーションをフォーム全体に対して有効にするためには、[[yii\widgets\ActiveForm::enableAjaxValidation]] プロパティを `true` に設定して、`id` にフォームを特定するユニークな ID を設定しなければなりません。 |
||||
|
||||
```php |
||||
<?php $form = yii\widgets\ActiveForm::begin([ |
||||
'id' => 'contact-form', |
||||
'enableAjaxValidation' => true, |
||||
]); ?> |
||||
``` |
||||
|
||||
個別の入力フィールドについても、[[yii\widgets\ActiveField::enableAjaxValidation]] プロパティを設定して、AJAX バリデーションを有効にしたり無効にしたりすることが出来ます。 |
||||
|
||||
また、サーバ側では、AJAX バリデーションのリクエストを処理できるように準備しておく必要があります。 |
||||
これは、コントローラのアクションにおいて、次のようなコード断片を使用することで達成できます。 |
||||
|
||||
```php |
||||
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) { |
||||
Yii::$app->response->format = Response::FORMAT_JSON; |
||||
return ActiveForm::validate($model); |
||||
} |
||||
``` |
||||
|
||||
上記のコードは、現在のリクエストが AJAX であるかどうかをチェックします。 |
||||
もし AJAX であるなら、リクエストに応えてバリデーションを実行し、エラーを JSON 形式で返します。 |
||||
|
||||
> Info|情報: AJAX バリデーションを実行するためには、[Deferred バリデーション](#deferred-validation) を使うことも出来ます。 |
||||
しかし、ここで説明された AJAX バリデーションの機能の方がより体系化されており、コーディングの労力も少なくて済みます。 |
@ -0,0 +1,98 @@
|
||||
クライアントスクリプトを扱う |
||||
============================ |
||||
|
||||
> Note|注意: この節はまだ執筆中です。 |
||||
|
||||
### スクリプトを登録する |
||||
|
||||
[[yii\web\View]] オブジェクトに対してスクリプトを登録することが出来ます。 |
||||
このための専用のメソッドが二つあります。 |
||||
すなわち、インラインスクリプトのための [[yii\web\View::registerJs()|registerJs()]] と、外部スクリプトのための [[yii\web\View::registerJsFile()|registerJsFile()]] です。 |
||||
インラインスクリプトは、設定のためや、動的に生成されるコードのために有用なものです。 |
||||
次のようにして、これらを追加するメソッドを使うことが出来ます。 |
||||
|
||||
```php |
||||
$this->registerJs("var options = ".json_encode($options).";", View::POS_END, 'my-options'); |
||||
``` |
||||
|
||||
最初の引数は、ページに挿入したい実際の JS コードです。 |
||||
二番目の引数は、スクリプトがページのどの場所に挿入されるべきかを決定します。 |
||||
取りうる値は以下のとおりです。 |
||||
|
||||
- [[yii\web\View::POS_HEAD|View::POS_HEAD]] - head セクション。 |
||||
- [[yii\web\View::POS_BEGIN|View::POS_BEGIN]] - 開始の `<body>` の直後。 |
||||
- [[yii\web\View::POS_END|View::POS_END]] - 終了の `</body>` の直前。 |
||||
- [[yii\web\View::POS_READY|View::POS_READY]] - ドキュメントの `ready` イベントで実行するコード。これを指定すると、[[yii\web\JqueryAsset|jQuery]] が自動的に登録されます。 |
||||
- [[yii\web\View::POS_LOAD|View::POS_LOAD]] - ドキュメントの `load` イベントで実行するコード。これを指定すると、[[yii\web\JqueryAsset|jQuery]] が自動的に登録されます。 |
||||
|
||||
最後の引数は、スクリプトのユニークな ID です。これによってコードブロックを一意に特定し、同じ ID のスクリプトが既にある場合は、新しいものを追加するのでなく、それを置き換えます。 |
||||
ID を指定しない場合は、JS コードそれ自身が ID として扱われます。 |
||||
|
||||
外部スクリプトは次のようにして追加することが出来ます。 |
||||
|
||||
```php |
||||
$this->registerJsFile('http://example.com/js/main.js', ['depends' => [JqueryAsset::className()]]); |
||||
``` |
||||
|
||||
[[yii\web\View::registerJsFile()|registerJsFile()]] の引数は [[yii\web\View::registerCssFile()|registerCssFile()]] のそれと同じです。 |
||||
上記の例では、`main.js` ファイルを `JqueryAsset` に依存するものとして登録しています。 |
||||
これは、`main.js` ファイルが `jquery.js` の後に追加されるようになることを意味します。 |
||||
この依存関係を指定しない場合は、`main.js` と `jquery.js` の相対的な順序は未定義となります。 |
||||
|
||||
[[yii\web\View::registerCssFile()|registerCssFile()]] と同じように、外部 JS ファイルを登録するのに [[yii\web\View::registerJsFile()|registerJsFile()]] を使わずに、[アセットバンドル](structure-assets.md) を使うことが強く推奨されます。 |
||||
|
||||
|
||||
### アセットバンドルを登録する |
||||
|
||||
既に述べたように、CSS と JavaScript を直接に使う代りにアセットバンドルを使うことが望まれます。 |
||||
アセットバンドルを定義する方法の詳細は、ガイドの [アセットマネージャ](structure-assets.md) の節で知ることが出来ます。 |
||||
既に定義されているアセットバンドルを使うことについては、次のように非常に簡明です。 |
||||
|
||||
```php |
||||
\frontend\assets\AppAsset::register($this); |
||||
``` |
||||
|
||||
|
||||
|
||||
### CSS を登録する |
||||
|
||||
[[yii\web\View::registerCss()|registerCss()]] またはr [[yii\web\View::registerCssFile()|registerCssFile()]] を使って CSS を登録することが出来ます。 |
||||
前者は CSS のコードブロックを登録し、後者は外部 CSS ファイルを登録します。 |
||||
例えば、 |
||||
|
||||
```php |
||||
$this->registerCss("body { background: #f00; }"); |
||||
``` |
||||
|
||||
上記のコードは、以下の内容をページの head セクションに追加する結果となります。 |
||||
|
||||
```html |
||||
<style> |
||||
body { background: #f00; } |
||||
</style> |
||||
``` |
||||
|
||||
`style` タグに追加のプロパティを指定したい場合は、三番目の引数として「名前-値」のペアの配列を渡します。 |
||||
`style` タグが一つだけになることを保証する必要がある場合は、メタタグの説明で言及したように、4番目の引数を使います。 |
||||
|
||||
```php |
||||
$this->registerCssFile("http://example.com/css/themes/black-and-white.css", [ |
||||
'depends' => [BootstrapAsset::className()], |
||||
'media' => 'print', |
||||
], 'css-print-theme'); |
||||
``` |
||||
|
||||
上記のコードは、ページの head セクションに CSS ファイルへのリンクを追加します。 |
||||
|
||||
* 最初の引数が登録されるべき CSS ファイルを指定します。 |
||||
* 二番目の引数は、結果として生成される `<link>` タグの HTML 属性を指定するものです。 |
||||
ただし、`depends` オプションは特別な処理を受けます。 |
||||
このオプションは、この CSS ファイルが依存するアセットバンドルを指定するものです。 |
||||
この例では、依存するアセットバンドルは [[yii\bootstrap\BootstrapAsset|BootstrapAsset]] です。 |
||||
これは、この CSS ファイルが、[[yii\bootstrap\BootstrapAsset|BootstrapAsset]] に含まれる CSS ファイルの *後* に追加されることを意味します。 |
||||
* 最後の引数は、この CSS ファイルを特定するための ID を指定するものです。 |
||||
指定されていない場合は、CSS ファイルの URL が代りに ID として使用されます。 |
||||
|
||||
外部 CSS ファイルを登録するためには、[[yii\web\View::registerCssFile()|registerCssFile()]] を使うのではなく、[アセットバンドル](structure-assets.md) を使うことが強く推奨されます。 |
||||
アセットバンドルを使うと、複数の CSS ファイルを結合して圧縮することが可能になります。 |
||||
トラフィックの多いウェブサイトではそうすることが望まれます。 |
@ -0,0 +1,133 @@
|
||||
データプロバイダ |
||||
================ |
||||
|
||||
> Note|注意: この節はまだ執筆中です。 |
||||
|
||||
データプロバイダは、 [[yii\data\DataProviderInterface]] によってデータセットを抽象化し、ページネーションと並べ替えを処理します。 |
||||
[グリッドやリストなどのデータウィジェット](output-data-widgets.md) で使用することが出来ます。 |
||||
|
||||
Yii は三つのデータプロバイダを内蔵しています。すなわち、[[yii\data\ActiveDataProvider]]、[[yii\data\ArrayDataProvider]] そして [[yii\data\SqlDataProvider]] です。 |
||||
|
||||
アクティブデータプロバイダ |
||||
-------------------------- |
||||
|
||||
`ActiveDataProvider` は [[yii\db\Query]] および [[yii\db\ActiveQuery]] を使って DB クエリを実行して、データを提供します。 |
||||
|
||||
次のコードは、これを使って、ActiveRecord のインスタンスを提供する例です。 |
||||
|
||||
```php |
||||
$provider = new ActiveDataProvider([ |
||||
'query' => Post::find(), |
||||
'pagination' => [ |
||||
'pageSize' => 20, |
||||
], |
||||
]); |
||||
|
||||
// 現在のページの投稿を取得する |
||||
$posts = $provider->getModels(); |
||||
``` |
||||
|
||||
そして次の例は、ActiveRecord なしで ActiveDataProvider を使う方法を示すものです。 |
||||
|
||||
```php |
||||
$query = new Query(); |
||||
$provider = new ActiveDataProvider([ |
||||
'query' => $query->from('post'), |
||||
'sort' => [ |
||||
// デフォルトのソートを name ASC, created_at DESC とする |
||||
'defaultOrder' => [ |
||||
'name' => SORT_ASC, |
||||
'created_at' => SORT_DESC |
||||
] |
||||
], |
||||
'pagination' => [ |
||||
'pageSize' => 20, |
||||
], |
||||
]); |
||||
|
||||
// 現在のページの投稿を取得する |
||||
$posts = $provider->getModels(); |
||||
``` |
||||
|
||||
配列データプロバイダ |
||||
-------------------- |
||||
|
||||
ArrayDataProvider はデータの配列に基づいたデータプロバイダを実装するものです。 |
||||
|
||||
[[yii\data\ArrayDataProvider::$allModels]] プロパティが、並べ替えやページネーションの対象となるデータの全てのモデルを含みます。 |
||||
ArrayDataProvider は、並べ替えとページネーションを実行した後に、データを提供します。 |
||||
[[yii\data\ArrayDataProvider::$sort]] および [[yii\data\ArrayDataProvider::$pagination]] のプロパティを構成して、並べ替えとページネーションの動作をカスタマイズすることが出来ます。 |
||||
|
||||
[[yii\data\ArrayDataProvider::$allModels]] 配列の要素は、オブジェクト (例えば、モデルのオブジェクト) であるか、連想配列 (例えば、DAO のクエリ結果) であるかの、どちらかです。 |
||||
[[yii\data\ArrayDataProvider::$key]] プロパティには、必ず、データレコードを一意的に特定出来るフィールドの名前をセットするか、そのようなフィールドがない場合は `false` をセットするかしなければなりません。 |
||||
|
||||
`ActiveDataProvider` と比較すると、`ArrayDataProvider` は、[[yii\data\ArrayDataProvider::$allModels]] を準備して持たなければならないため、効率が良くありません。 |
||||
|
||||
`ArrayDataProvider` は次のようにして使用することが出来ます。 |
||||
|
||||
```php |
||||
$query = new Query(); |
||||
$provider = new ArrayDataProvider([ |
||||
'allModels' => $query->from('post')->all(), |
||||
'sort' => [ |
||||
'attributes' => ['id', 'username', 'email'], |
||||
], |
||||
'pagination' => [ |
||||
'pageSize' => 10, |
||||
], |
||||
]); |
||||
// 現在のページの投稿を取得する |
||||
$posts = $provider->getModels(); |
||||
``` |
||||
|
||||
> Note|注意: 並べ替えの機能を使いたいときは、どのカラムがソート出来るかをプロバイダが知ることが出来るように、[[sort]] プロパティを構成しなければなりません。 |
||||
|
||||
SQL データプロバイダ |
||||
-------------------- |
||||
|
||||
SqlDataProvider は、素の SQL 文に基づいたデータプロバイダを実装するものです。 |
||||
これは、各要素がクエリ結果の行を表す配列の形式でデータを提供します。 |
||||
|
||||
他のプロバイダ同様に、SqlDataProvider も、並べ替えとページネーションをサポートしています。 |
||||
並べ替えとページネーションは、与えられた [[yii\data\SqlDataProvider::$sql]] 文を "ORDER BY" 句および "LIMIT" 句で修正することによって実行されます。 |
||||
[[yii\data\SqlDataProvider::$sort]] および [[yii\data\SqlDataProvider::$pagination]] のプロパティを構成して、並べ替えとページネーションの動作をカスタマイズすることが出来ます。 |
||||
|
||||
`SqlDataProvider` は次のようにして使用することが出来ます。 |
||||
|
||||
```php |
||||
$count = Yii::$app->db->createCommand(' |
||||
SELECT COUNT(*) FROM user WHERE status=:status |
||||
', [':status' => 1])->queryScalar(); |
||||
|
||||
$dataProvider = new SqlDataProvider([ |
||||
'sql' => 'SELECT * FROM user WHERE status=:status', |
||||
'params' => [':status' => 1], |
||||
'totalCount' => $count, |
||||
'sort' => [ |
||||
'attributes' => [ |
||||
'age', |
||||
'name' => [ |
||||
'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC], |
||||
'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC], |
||||
'default' => SORT_DESC, |
||||
'label' => 'Name', |
||||
], |
||||
], |
||||
], |
||||
'pagination' => [ |
||||
'pageSize' => 20, |
||||
], |
||||
]); |
||||
|
||||
// 現在のページの user のレコードを取得する |
||||
$models = $dataProvider->getModels(); |
||||
``` |
||||
|
||||
> Note|注意: ページネーションの機能を使いたい場合は、[[yii\data\SqlDataProvider::$totalCount]] プロパティに (ページネーション無しの) 総行数を設定しなければなりません。 |
||||
そして、並べ替えの機能を使いたい場合は、どのカラムがソート出来るかをプロバイダが知ることが出来るように、[[yii\data\SqlDataProvider::$sort]] プロパティを構成しなければなりません。 |
||||
|
||||
|
||||
あなた自身のカスタムデータプロバイダを実装する |
||||
---------------------------------------------- |
||||
|
||||
(内容未定) |
@ -0,0 +1,517 @@
|
||||
データウィジェット |
||||
================== |
||||
|
||||
> Note|注意: この節はまだ執筆中です。 |
||||
|
||||
ListView |
||||
-------- |
||||
|
||||
|
||||
|
||||
DetailView |
||||
---------- |
||||
|
||||
DetailView は単一のデータ [[yii\widgets\DetailView::$model|モデル]] の詳細を表示します。 |
||||
|
||||
モデルを標準的な書式で表示する場合 (例えば、全てのモデル属性をそれぞれテーブルの一行として表示する場合) に最も適しています。 |
||||
モデルは [[\yii\base\Model]] のインスタンスか連想配列かのどちらかを取り得ます。 |
||||
|
||||
DetailView は [[yii\widgets\DetailView::$attributes]] プロパティを使って、モデルのどの属性をどのように表示すべきかを決定します。 |
||||
|
||||
次に DetailView の典型的な用例を示します。 |
||||
|
||||
```php |
||||
echo DetailView::widget([ |
||||
'model' => $model, |
||||
'attributes' => [ |
||||
'title', // title 属性 (平文テキストで) |
||||
'description:html', // description 属性は HTML |
||||
[ // モデルの所有者の名前 |
||||
'label' => '所有者', |
||||
'value' => $model->owner->name, |
||||
], |
||||
], |
||||
]); |
||||
``` |
||||
|
||||
|
||||
GridView |
||||
-------- |
||||
|
||||
データグリッドすなわち GridView は Yii の最も強力なウィジェットの一つです。 |
||||
これは、システムの管理セクションを素速く作らねばならない時に、この上なく便利なものです。 |
||||
このウィジェットは [データプロバイダ](output-data-providers.md) からデータを受けて、テーブルの形式で、行ごとに一組のカラムを使ってデータを表示します。 |
||||
|
||||
テーブルの各行が一つのデータアイテムを表し、カラムは通常はアイテムの属性を表します (カラムの中には、複数の属性を組み合わせた複雑な式に対応するものや、静的なテキストを表すものもあります)。 |
||||
|
||||
グリッドビューはデータアイテムの並べ替えとページネーションの両方をサポートします。 |
||||
並べ替えとページネーションは、AJAX モードで、あるいは、通常のページリクエストとして、実行することが出来ます。 |
||||
GridView を使用することの利点は、ユーザが JavaScript を無効化しても、並べ替えとページネーションが自動的に通常のページリクエストにグレードダウンして、引き続き期待通りの動作をするという点にあります。 |
||||
|
||||
GridView を使うために必要な最小限のコードは次のようなものです。 |
||||
|
||||
```php |
||||
use yii\grid\GridView; |
||||
use yii\data\ActiveDataProvider; |
||||
|
||||
$dataProvider = new ActiveDataProvider([ |
||||
'query' => Post::find(), |
||||
'pagination' => [ |
||||
'pageSize' => 20, |
||||
], |
||||
]); |
||||
echo GridView::widget([ |
||||
'dataProvider' => $dataProvider, |
||||
]); |
||||
``` |
||||
|
||||
上記のコードは、最初にデータプロバイダを作成し、次に GridView を使って、データプロバイダから受け取る全ての行の全ての属性を表示するものです。 |
||||
そして、表示されたテーブルには、並べ替えとページネーションの機能が装備されます。 |
||||
|
||||
### グリッドカラム |
||||
|
||||
Yii のグリッドは数多くのカラムから構成されます。 |
||||
それらのカラムは、タイプや設定に応じて、さまざまな形でデータを表示することが出来ます。 |
||||
|
||||
カラムは、GridView の構成情報の `columns` の部分において、以下のように定義されます。 |
||||
|
||||
```php |
||||
echo GridView::widget([ |
||||
'dataProvider' => $dataProvider, |
||||
'columns' => [ |
||||
['class' => 'yii\grid\SerialColumn'], |
||||
// $dataProvider に含まれるデータによって定義される単純なカラム |
||||
// モデルのカラムのデータが使われる |
||||
'id', |
||||
'username', |
||||
// 複雑なカラム定義 |
||||
[ |
||||
'class' => 'yii\grid\DataColumn', // 省略可。これが既定値。 |
||||
'value' => function ($data) { |
||||
return $data->name; // 配列データの場合は $data['name']。例えば、SqlDataProvider を使う場合。 |
||||
}, |
||||
], |
||||
], |
||||
]); |
||||
``` |
||||
|
||||
構成情報の `columns` の部分が指定されない場合は、Yii は、可能な限り、データプロバイダのモデルの全てのカラムを表示しようとすることに注意してください。 |
||||
|
||||
### カラムクラス |
||||
|
||||
グリッドのカラムは、いろいろなカラムクラスを使うことでカスタマイズすることが出来ます。 |
||||
|
||||
```php |
||||
echo GridView::widget([ |
||||
'dataProvider' => $dataProvider, |
||||
'columns' => [ |
||||
[ |
||||
'class' => 'yii\grid\SerialColumn', // <-- ここ |
||||
// ここで追加のプロパティを構成することが出来ます |
||||
], |
||||
``` |
||||
|
||||
Yii によって提供されるカラムクラスを以下で見ていきますが、それらに加えて、あなた自身のカラムクラスを作成することも出来ます。 |
||||
|
||||
全てのカラムクラスは [[\yii\grid\Column]] を拡張するものですので、グリッドのカラムを構成するときに設定できる共通のオプションがいくつかあります。 |
||||
|
||||
- `header` によって、ヘッダ行のコンテントを設定することが出来ます。 |
||||
- `footer` によって、振った行のコンテントを設定することが出来ます。 |
||||
- `visible` はカラムの可視性を定義します。 |
||||
- `content` によって、行のデータを返す有効な PHP コールバックを渡すことが出来ます。書式は以下の通りです。 |
||||
|
||||
```php |
||||
function ($model, $key, $index, $column) { |
||||
return '文字列'; |
||||
} |
||||
``` |
||||
|
||||
下記のオプションに配列を渡して、コンテナ要素のさまざまな HTML オプションを指定することが出来ます。 |
||||
|
||||
- `headerOptions` |
||||
- `footerOptions` |
||||
- `filterOptions` |
||||
- `contentOptions` |
||||
|
||||
#### データカラム <a name="data-column"></a> |
||||
|
||||
データカラムは、データの表示と並べ替えに使用されます。 |
||||
これがデフォルトのカラムタイプですので、これを使用するときはクラスの指定を省略することが出来ます。 |
||||
|
||||
データカラムの主要な設定項目は、その書式です。 |
||||
書式は `format` 属性によって定義することが出来ます。 |
||||
その値は、デフォルトでは [[\yii\i18n\Formatter|Formatter]] である `formatter` [アプリケーションコンポーネント](structure-application-components.md) のメソッドと対応するものです。 |
||||
|
||||
```php |
||||
echo GridView::widget([ |
||||
'columns' => [ |
||||
[ |
||||
'attribute' => 'name', |
||||
'format' => 'text' |
||||
], |
||||
[ |
||||
'attribute' => 'birthday', |
||||
'format' => ['date', 'php:Y-m-d'] |
||||
], |
||||
], |
||||
]); |
||||
``` |
||||
|
||||
上記において、`text` は [[\yii\i18n\Formatter::asText()]] に対応し、カラムの値が最初の引数として渡されます。 |
||||
二番目のカラムの定義では、`date` が [[\yii\i18n\Formatter::asDate()]] に対応します。 |
||||
カラムの値が、ここでも、最初の引数として渡され、'php:Y-m-d' が二番目の引数の値として渡されます。 |
||||
|
||||
利用できるフォーマッタの一覧については、[データのフォーマット](output-formatter.md) の節を参照してください。 |
||||
|
||||
|
||||
#### アクションカラム |
||||
|
||||
アクションカラムは、各行について、更新や削除などのアクションボタンを表示します。 |
||||
|
||||
```php |
||||
echo GridView::widget([ |
||||
'dataProvider' => $dataProvider, |
||||
'columns' => [ |
||||
[ |
||||
'class' => 'yii\grid\ActionColumn', |
||||
// ここで追加のプロパティを構成することが出来ます |
||||
], |
||||
``` |
||||
|
||||
構成が可能なプロパティは、以下の通りです。 |
||||
|
||||
- `controller` は、アクションを処理すべきコントローラの ID です。設定されていない場合は、現在アクティブなコントローラが使われます。 |
||||
- `template` は、アクションカラムの各セルを構成するのに使用されるテンプレートを定義します。 |
||||
波括弧に囲まれたトークンは、コントローラのアクション ID として扱われます (アクションカラムのコンテキストでは *ボタンの名前* とも呼ばれます)。 |
||||
これらは、[[yii\grid\ActionColumn::$buttons|buttons]] によって定義される、対応するボタン表示コールバックによって置き換えられます。 |
||||
例えば、`{view}` というトークンは、`buttons['view']` のコールバックの結果によって置き換えられます。 |
||||
コールバックが見つからない場合は、トークンは空文字列によって置き換えられます。 |
||||
デフォルトのテンプレートは `{view} {update} {delete}` です。 |
||||
- `buttons` はボタン表示コールバックの配列です。 |
||||
配列のキーはボタンの名前 (波括弧を除く) であり、値は対応するボタン表示コールバックです。 |
||||
コールバックは下記のシグニチャを使わなければなりません。 |
||||
|
||||
```php |
||||
function ($url, $model, $key) { |
||||
// ボタンの HTML コードを返す |
||||
} |
||||
``` |
||||
|
||||
上記のコードで、`$url` はカラムがボタンのために生成する URL、`$model` は現在の行に表示されるモデルオブジェクト、そして `$key` はデータプロバイダ配列のモデルのキーです。 |
||||
|
||||
- `urlCreator` は、指定されたモデルの情報を使って、ボタンの URL を生成するコールバックです。 |
||||
コールバックのシグニチャは [[yii\grid\ActionColumn::createUrl()]] のそれと同じでなければなりません。 |
||||
このプロパティが設定されていないときは、ボタンの URL は [[yii\grid\ActionColumn::createUrl()]] を使って生成されます。 |
||||
|
||||
#### チェックボックスカラム |
||||
|
||||
CheckboxColumn はチェックボックスのカラムを表示します。 |
||||
|
||||
[[yii\grid\GridView]] に CheckboxColumn を追加するためには、以下のようにして、[[yii\grid\GridView::$columns|columns]] 構成情報にカラムを追加します。 |
||||
|
||||
```php |
||||
echo GridView::widget([ |
||||
'dataProvider' => $dataProvider, |
||||
'columns' => [ |
||||
// ... |
||||
[ |
||||
'class' => 'yii\grid\CheckboxColumn', |
||||
// ここで追加のプロパティを構成することが出来ます |
||||
], |
||||
], |
||||
``` |
||||
|
||||
ユーザはチェックボックスをクリックして、グリッドの行を選択することが出来ます。 |
||||
選択された行は、次の JavaScript コードを呼んで取得することが出来ます。 |
||||
|
||||
```javascript |
||||
var keys = $('#grid').yiiGridView('getSelectedRows'); |
||||
// keys は選択された行と関連付けられたキーの配列 |
||||
``` |
||||
|
||||
#### シリアルカラム |
||||
|
||||
シリアルカラムは、`1` から始まる行番号を表示します。 |
||||
|
||||
使い方は、次のように、とても簡単です。 |
||||
|
||||
```php |
||||
echo GridView::widget([ |
||||
'dataProvider' => $dataProvider, |
||||
'columns' => [ |
||||
['class' => 'yii\grid\SerialColumn'], // <-- ここ |
||||
// ... |
||||
``` |
||||
|
||||
|
||||
### データを並べ替える |
||||
|
||||
- https://github.com/yiisoft/yii2/issues/1576 |
||||
|
||||
### データをフィルタリングする |
||||
|
||||
データをフィルタリングするためには、GridView は、フィルタリングのフォームから入力を受け取り、検索基準に合わせてデータプロバイダのクエリを調整するための [モデル](structure-models.md) を必要とします。 |
||||
[アクティブレコード](db-active-record.md) を使用している場合は、必要な機能を提供する検索用のモデルクラスを作成するのが一般的なプラクティスです (あなたに代って Gii が生成してくれます)。 |
||||
このクラスは、検索のためのバリデーション規則を定義し、データプロバイダを返す `search()` メソッドを提供するものです。 |
||||
|
||||
`Post` モデルに対して検索機能を追加するために、次の例のようにして、`PostSearch` モデルを作成することが出来ます。 |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
namespace app\models; |
||||
|
||||
use Yii; |
||||
use yii\base\Model; |
||||
use yii\data\ActiveDataProvider; |
||||
|
||||
class PostSearch extends Post |
||||
{ |
||||
public function rules() |
||||
{ |
||||
// rules() にあるフィールドだけが検索可能 |
||||
return [ |
||||
[['id'], 'integer'], |
||||
[['title', 'creation_date'], 'safe'], |
||||
]; |
||||
} |
||||
|
||||
public function scenarios() |
||||
{ |
||||
// 親クラスの scenarios() の実装をバイパスする |
||||
return Model::scenarios(); |
||||
} |
||||
|
||||
public function search($params) |
||||
{ |
||||
$query = Post::find(); |
||||
|
||||
$dataProvider = new ActiveDataProvider([ |
||||
'query' => $query, |
||||
]); |
||||
|
||||
// 検索フォームのデータをロードして検証する |
||||
if (!($this->load($params) && $this->validate())) { |
||||
return $dataProvider; |
||||
} |
||||
|
||||
// フィルタを追加してクエリを調整する |
||||
$query->andFilterWhere(['id' => $this->id]); |
||||
$query->andFilterWhere(['like', 'title', $this->title]) |
||||
->andFilterWhere(['like', 'creation_date', $this->creation_date]); |
||||
|
||||
return $dataProvider; |
||||
} |
||||
} |
||||
|
||||
``` |
||||
|
||||
この `search()` メソッドをコントローラで使用して、GridView のためのデータプロバイダを取得することが出来ます。 |
||||
|
||||
```php |
||||
$searchModel = new PostSearch(); |
||||
$dataProvider = $searchModel->search(Yii::$app->request->get()); |
||||
|
||||
return $this->render('myview', [ |
||||
'dataProvider' => $dataProvider, |
||||
'searchModel' => $searchModel, |
||||
]); |
||||
``` |
||||
|
||||
そしてビューでは、`$dataProvider` と `$searchModel` を GridView に与えます。 |
||||
|
||||
```php |
||||
echo GridView::widget([ |
||||
'dataProvider' => $dataProvider, |
||||
'filterModel' => $searchModel, |
||||
]); |
||||
``` |
||||
|
||||
|
||||
### モデルのリレーションを扱う |
||||
|
||||
GridView でアクティブレコードを表示するときに、リレーションのカラムの値、例えば、単に投稿者の `id` というのではなく、投稿者の名前を表示するという場合に遭遇するかも知れません。 |
||||
`Post` モデルが `author` という名前のリレーションを持っていて、その投稿者のモデルが `name` という属性を持っているなら、カラムの属性名を `author.name` と定義します。 |
||||
そうすれば、GridView が投稿者の名前を表示するようになります。 |
||||
ただし、並べ替えとフィルタリングは、既定では有効になりません。 |
||||
これらの機能を追加するためには、前の項で導入した `PostSearch` モデルを調整しなければなりません。 |
||||
|
||||
リレーションのカラムによる並べ替えを有効にするためには、リレーションのテーブルを結合し、データプロバイダの Sort コンポーネントに並べ替えの規則を追加します。 |
||||
|
||||
```php |
||||
$query = Post::find(); |
||||
$dataProvider = new ActiveDataProvider([ |
||||
'query' => $query, |
||||
]); |
||||
|
||||
// リレーション `author` を結合します。これはテーブル `users` に対するリレーションであり、 |
||||
// テーブルエイリアスを `author` とします。 |
||||
$query->joinWith(['author' => function($query) { $query->from(['author' => 'users']); }]); |
||||
// リレーションのカラムによる並べ替えを有効にします。 |
||||
$dataProvider->sort->attributes['author.name'] = [ |
||||
'asc' => ['author.name' => SORT_ASC], |
||||
'desc' => ['author.name' => SORT_DESC], |
||||
]; |
||||
|
||||
// ... |
||||
``` |
||||
|
||||
フィルタリングも上記と同じ joinWith の呼び出しを必要とします。また、次のように、`attributes` と `rules` の中で、検索可能なカラムを追加で定義する必要があります。 |
||||
|
||||
```php |
||||
public function attributes() |
||||
{ |
||||
// 検索可能な属性にリレーションのフィールドを追加する |
||||
return array_merge(parent::attributes(), ['author.name']); |
||||
} |
||||
|
||||
public function rules() |
||||
{ |
||||
return [ |
||||
[['id'], 'integer'], |
||||
[['title', 'creation_date', 'author.name'], 'safe'], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
`search()` メソッドでは、次のように、もう一つのフィルタ条件を追加するだけです。 |
||||
|
||||
```php |
||||
$query->andFilterWhere(['LIKE', 'author.name', $this->getAttribute('author.name')]); |
||||
``` |
||||
|
||||
> Info|情報: 上の例では、リレーション名とテーブルエイリアスに同じ文字列を使用しています。 |
||||
> しかし、エイリアスとリレーション名が異なる場合は、どこでエイリアスを使い、どこでリレーション名を使うかに注意を払わなければなりません。 |
||||
> これに関する簡単な規則は、データベースクエリを構築するために使われる全ての場所でエイリアスを使い、`attributes()` や `rules()` など、その他の全ての定義においてリレーション名を使う、というものです。 |
||||
> |
||||
> 例えば、投稿者のリレーションテーブルに `au` というエイリアスを使う場合は、joinWith の文は以下のようになります。 |
||||
> |
||||
> ```php |
||||
> $query->joinWith(['author' => function($query) { $query->from(['au' => 'users']); }]); |
||||
> ``` |
||||
> リレーションの定義においてエイリアスが定義されている場合は、単に `$query->joinWith(['author']);` として呼び出すことも可能です。 |
||||
> |
||||
> フィルタ条件においてはエイリアスが使われなければなりませんが、属性の名前はリレーション名のままで変りません。 |
||||
> |
||||
> ```php |
||||
> $query->andFilterWhere(['LIKE', 'au.name', $this->getAttribute('author.name')]); |
||||
> ``` |
||||
> |
||||
> 並べ替えの定義についても同じことです。 |
||||
> |
||||
> ```php |
||||
> $dataProvider->sort->attributes['author.name'] = [ |
||||
> 'asc' => ['au.name' => SORT_ASC], |
||||
> 'desc' => ['au.name' => SORT_DESC], |
||||
> ]; |
||||
> ``` |
||||
> |
||||
> さらに、並べ替えの [[yii\data\Sort::defaultOrder|defaultOrder]] を指定するときも、エイリアスではなくリレーション名を使う必要があります。 |
||||
> |
||||
> ```php |
||||
> $dataProvider->sort->defaultOrder = ['author.name' => SORT_ASC]; |
||||
> ``` |
||||
|
||||
> Info|情報: `joinWith` およびバックグラウンドで実行されるクエリの詳細については、[アクティブレコード - リレーションを使ってテーブルを結合する](db-active-record.md#joining-with-relations) を参照してください。 |
||||
|
||||
#### SQL ビューを使って、データのフィルタリング・並べ替え・表示をする |
||||
|
||||
もう一つ別に、もっと高速で便利な手法があります。SQL ビューです。 |
||||
例えば、ユーザとユーザのプロファイルを一緒にグリッドビューに表示する必要がある場合、次のような SQL ビューを作成することが出来ます。 |
||||
|
||||
```sql |
||||
CREATE OR REPLACE VIEW vw_user_info AS |
||||
SELECT user.*, user_profile.lastname, user_profile.firstname |
||||
FROM user, user_profile |
||||
WHERE user.id = user_profile.user_id |
||||
``` |
||||
|
||||
そして、このビューを表す ActiveRecord を作成します。 |
||||
|
||||
```php |
||||
|
||||
namespace app\models\views\grid; |
||||
|
||||
use yii\db\ActiveRecord; |
||||
|
||||
class UserView extends ActiveRecord |
||||
{ |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public static function tableName() |
||||
{ |
||||
return 'vw_user_info'; |
||||
} |
||||
|
||||
public static function primaryKey() |
||||
{ |
||||
return ['id']; |
||||
} |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function rules() |
||||
{ |
||||
return [ |
||||
// ここで規則を定義 |
||||
]; |
||||
} |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public static function attributeLabels() |
||||
{ |
||||
return [ |
||||
// ここで属性のラベルを定義 |
||||
]; |
||||
} |
||||
|
||||
|
||||
} |
||||
``` |
||||
|
||||
このようにした後は、この UserView アクティブレコードを検索用のモデルとともに使うことが出来ます。 |
||||
並べ替えやフィルタリングの属性を追加で定義する必要はありません。 |
||||
全ての属性がそのままで動作します。 |
||||
この手法にはいくつかの長所と短所があることに注意してください。 |
||||
|
||||
- 並べ替えとフィルタリングの条件をいろいろと定義する必要はありません。全てそのままで動きます。- データサイズが小さく、実行される SQL クエリの数が少ない (通常なら全てのリレーションについて一つずつ必要になる追加のクエリが要らない) ため、非常に高速になり得ます。 |
||||
- これは SQL ビューにかぶせた単純な UI に過ぎないもので、エンティティに含まれるドメインロジックを欠いています。 |
||||
従って、`isActive` や `isDeleted` などのような UI に影響するメソッドがある場合は、それらをこのクラスの中に複製する必要があります。 |
||||
|
||||
|
||||
### 一つのページに複数のグリッドビュー |
||||
|
||||
単一のページで二つ以上のグリッドビューを使うことが出来ますが、お互いが干渉しないように、追加の構成がいくつか必要になります。 |
||||
グリッドビューの複数のインスタンスを使う場合は、並べ替えとページネーションのリンクが違うパラメータ名を持って生成されるように構成して、それぞれのグリッドビューが独立した並べ替えとページネーションを持つことが出来るようにしなければなりません。 |
||||
そのためには、データプロバイダの [[yii\data\BaseDataProvider::$sort|sort]] と [[yii\data\BaseDataProvider::$pagination|pagination]] インスタンスの [[yii\data\Sort::sortParam|sortParam]] と [[yii\data\Pagination::pageParam|pageParam]] を設定します。 |
||||
|
||||
`Post` と `User` のリストを表示するために、二つのプロバイダ、`$userProvider` と `$postProvider` を準備済みであると仮定します。 |
||||
|
||||
```php |
||||
use yii\grid\GridView; |
||||
|
||||
$userProvider->pagination->pageParam = 'user-page'; |
||||
$userProvider->sort->sortParam = 'user-sort'; |
||||
|
||||
$postProvider->pagination->pageParam = 'post-page'; |
||||
$postProvider->sort->sortParam = 'post-sort'; |
||||
|
||||
echo '<h1>ユーザ</h1>'; |
||||
echo GridView::widget([ |
||||
'dataProvider' => $userProvider, |
||||
]); |
||||
|
||||
echo '<h1>投稿</h1>'; |
||||
echo GridView::widget([ |
||||
'dataProvider' => $postProvider, |
||||
]); |
||||
``` |
||||
|
||||
### GridView を Pjax とともに使う |
||||
|
||||
(内容未定) |
@ -0,0 +1,190 @@
|
||||
データフォーマッタ |
||||
================== |
||||
|
||||
出力をフォーマットするために、Yii はデータをユーザにとってより読みやすいものにするためのフォーマッタクラスを提供しています。 |
||||
デフォルトでは、[[yii\i18n\Formatter]] というヘルパクラスが、`formatter` という名前の [アプリケーションコンポーネント](structure-application-components.md) として登録されます。 |
||||
|
||||
このヘルパが、日付/時刻、数字、その他のよく使われる形式について、データをローカライズしてフォーマットするための一連のメソッドを提供します。 |
||||
フォーマッタは、二つの異なる方法で使うことが出来ます。 |
||||
|
||||
1. フォーマットメソッド (全て `as` という接頭辞を持ちます) を直接に使用する。 |
||||
|
||||
```php |
||||
echo Yii::$app->formatter->asDate('2014-01-01', 'long'); // 出力: January 1, 2014 |
||||
echo Yii::$app->formatter->asPercent(0.125, 2); // 出力: 12.50% |
||||
echo Yii::$app->formatter->asEmail('cebe@example.com'); // 出力: <a href="mailto:cebe@example.com">cebe@example.com</a> |
||||
echo Yii::$app->formatter->asBoolean(true); // 出力: Yes |
||||
// null 値の表示も処理します。 |
||||
echo Yii::$app->formatter->asDate(null); // 出力: (Not set) |
||||
``` |
||||
|
||||
2. [[yii\i18n\Formatter::format()|format()]] メソッドとフォーマット名を使う。 |
||||
[[yii\grid\GridView]] や [[yii\widgets\DetailView]] のようなウィジェットでは、構成情報でカラムのデータの書式を指定することが出来ますが、これらウィジェットでもこのメソッドが使われています。 |
||||
|
||||
```php |
||||
echo Yii::$app->formatter->format('2014-01-01', 'date'); // 出力: January 1, 2014 |
||||
// 配列を使って、フォーマットメソッドのパラメータを指定することも出来ます。 |
||||
// `2` は asPercent() メソッドの $decimals パラメータの値です。 |
||||
echo Yii::$app->formatter->format(0.125, ['percent', 2]); // 出力: 12.50% |
||||
``` |
||||
|
||||
|
||||
[PHP intl 拡張](http://php.net/manual/ja/book.intl.php) がインストールされているときは、フォーマッタの全ての出力がローカライズされます。 |
||||
これのために [[yii\i18n\Formatter::locale|locale]] プロパティを構成することが出来ます。 |
||||
これが構成されていないときは、アプリケーションの [[yii\base\Application::language|language]] がロケールとして用いられます。 |
||||
詳細は [国際化](tutorial-i18n.md) の節を参照してください。 |
||||
フォーマッタはロケールに従って、正しい日付や数字の形式を選択し、月や曜日の名称もカレントの言語に翻訳します。 |
||||
日付の形式は [[yii\i18n\Formatter::timeZone|timeZone]] によっても左右されます。 |
||||
[[yii\i18n\Formatter::timeZone|timeZone]] も、明示的に構成されていない場合は、アプリケーションの [[yii\base\Application::timeZone|timeZone]] から取られます。 |
||||
|
||||
例えば、日付のフォーマットを呼ぶと、ロケールによってさまざまな結果を出力します。 |
||||
|
||||
```php |
||||
Yii::$app->formatter->locale = 'en-US'; |
||||
echo Yii::$app->formatter->asDate('2014-01-01'); // 出力: January 1, 2014 |
||||
Yii::$app->formatter->locale = 'de-DE'; |
||||
echo Yii::$app->formatter->asDate('2014-01-01'); // 出力: 1. Januar 2014 |
||||
Yii::$app->formatter->locale = 'ru-RU'; |
||||
echo Yii::$app->formatter->asDate('2014-01-01'); // 出力: 1 января 2014 г. |
||||
Yii::$app->formatter->locale = 'ja-JP'; |
||||
echo Yii::$app->formatter->asDate('2014-01-01'); // 出力: 2014/01/01 |
||||
``` |
||||
|
||||
> Note|注意: フォーマットの仕方は、PHP とともにコンパイルされた ICU ライブラリのバージョンの違いによって異なる可能性がありますし、[PHP intl 拡張](http://php.net/manual/ja/book.intl.php) がインストールされているか否かという事実によっても異なってきます。 |
||||
> 従って、あなたのウェブサイトが全ての環境で同じ出力を表示することを保証するために、全ての環境に PHP intl 拡張をインストールして、ICU ライブラリのバージョンが同じであることを確認する事を推奨します。 |
||||
> [PHP 環境を国際化のために設定する](tutorial-i18n.md#setup-environment) も参照してください。 |
||||
|
||||
|
||||
フォーマッタを構成する <a name="configuring-format"></a> |
||||
---------------------- |
||||
|
||||
フォーマットメソッドによって使われるデフォルトの書式は、[[yii\i18n\Formatter|フォーマッタクラス]] のプロパティを使って調整することが出来ます。 |
||||
プロパティの値をアプリケーション全体にわたって調整するために、[アプリケーションの構成情報](concept-configurations.md#application-configurations) において、`formatter` コンポーネントを構成することが出来ます。 |
||||
構成の例を下記に示します。 |
||||
利用できるプロパティの詳細については、[[yii\i18n\Formatter|Formatter クラスの API ドキュメント]] と、後続の項を参照してください。 |
||||
|
||||
```php |
||||
'components' => [ |
||||
'formatter' => [ |
||||
'dateFormat' => 'dd.MM.yyyy', |
||||
'decimalSeparator' => ',', |
||||
'thousandSeparator' => ' ', |
||||
'currencyCode' => 'EUR', |
||||
], |
||||
], |
||||
``` |
||||
|
||||
日時の値をフォーマットする <a name="date-and-time"></a> |
||||
-------------------------- |
||||
|
||||
フォーマッタクラスは日時の値をフォーマットするさまざまなメソッドを提供しています。すなわち、 |
||||
|
||||
- [[yii\i18n\Formatter::asDate()|date]] - 値は日付としてフォーマットされます。例えば `2014/01/01`。 |
||||
- [[yii\i18n\Formatter::asTime()|time]] - 値は時刻としてフォーマットされます。例えば `14:23`。 |
||||
- [[yii\i18n\Formatter::asDatetime()|datetime]] - 値は日付および時刻としてフォーマットされます。例えば `2014/01/01 14:23`。 |
||||
- [[yii\i18n\Formatter::asTimestamp()|timestamp]] - 値は [unix タイムスタンプ](http://en.wikipedia.org/wiki/Unix_time) としてフォーマットされます。例えば `1412609982`。 |
||||
- [[yii\i18n\Formatter::asRelativeTime()|relativeTime]] - 値は、その日時と現在との間隔として、人間に分かりやすい言葉でフォーマットされます。例えば `1 時間後`。 |
||||
|
||||
[[yii\i18n\Formatter::asDate()|date]]、[[yii\i18n\Formatter::asTime()|time]]、[[yii\i18n\Formatter::asDatetime()|datetime]] メソッドの日時の書式は、フォーマッタのプロパティ [[yii\i18n\Formatter::$dateFormat|$dateFormat]]、[[yii\i18n\Formatter::$timeFormat|$timeFormat]]、[[yii\i18n\Formatter::$datetimeFormat|$datetimeFormat]] を構成することで、グローバルに指定することが出来ます。 |
||||
|
||||
デフォルトでは、フォーマッタが使う書式は、ショートカット形式で指定します。 |
||||
これは、日付と時刻をユーザの国と言語にとって一般的な形式でフォーマット出来るように、現在アクティブなロケールに従ってさまざまに解釈されるものです。 |
||||
四つの異なるショートカット形式が利用できます。 |
||||
|
||||
- `short` は、`en_GB` ロケールでは、例えば、日付を `06/10/2014`、時刻を `15:58` と表示します。 |
||||
- `medium` は、 `6 Oct 2014` および `15:58:42`、 |
||||
- `long` は、`6 October 2014` および `15:58:42 GMT`、 |
||||
- そして `full` は `Monday, 6 October 2014` および `15:58:42 GMT` を表示します。 |
||||
|
||||
> Info:情報| `ja_JP` ロケールでは、次のようになります。 |
||||
> |
||||
> - `short` ... `2014/10/06` および `15:58` |
||||
> - `medium` ... `2014/10/06` および `15:58:42` |
||||
> - `long` ... `2014年10月6日` および `15:58:42 JST` |
||||
> - `full` ... `2014年10月6日月曜日` および `15時58分42秒 日本標準時` |
||||
|
||||
これに加えて、[ICU プロジェクト](http://site.icu-project.org/) によって定義された構文を使うカスタム書式を指定することが出来ます。 |
||||
この構文を説明する ICU マニュアルが下記の URL にあります: <http://userguide.icu-project.org/formatparse/datetime>。 |
||||
別の選択肢として、`php:` という接頭辞を付けた文字列を使って、PHP の [date()](http://php.net/manual/ja/function.date.php) 関数が認識する構文を使うことも出来ます。 |
||||
|
||||
```php |
||||
// ICU 形式 |
||||
echo Yii::$app->formatter->asDate('now', 'yyyy-MM-dd'); // 2014-10-06 |
||||
// PHP date() 形式 |
||||
echo Yii::$app->formatter->asDate('now', 'php:Y-m-d'); // 2014-10-06 |
||||
``` |
||||
|
||||
### タイムゾーン <a name="time-zones"></a> |
||||
|
||||
日時の値をフォーマットするときに、Yii はその値を [[yii\i18n\Formatter::timeZone|設定されたタイムゾーン]] に変換します。 |
||||
従って、入力値は、タイムゾーンが明示的に指定されていなければ、UTC であると見なされます。 |
||||
この理由により、全ての日時の値を UTC、それも、なるべくなら、定義によって UTC であることが保証されている UNIX タイムスタンプで保存することが推奨されます。 |
||||
入力値が UTC とは異なるタイムゾーンに属する場合は、次の例のように、タイムゾーンを明示的に記述しなければなりません。 |
||||
|
||||
```php |
||||
// Yii::$app->timeZone は 'Asia/Tokyo' であるとします。 |
||||
echo Yii::$app->formatter->asTime(1412599260); // 21:41:00 |
||||
echo Yii::$app->formatter->asTime('2014-10-06 12:41:00'); // 21:41:00 |
||||
echo Yii::$app->formatter->asTime('2014-10-06 21:41:00 JST'); // 21:41:00 |
||||
``` |
||||
|
||||
バージョン 2.0.1 からは、上記のコードの二番目の例のようにタイムゾーン識別子を含まないタイムスタンプに対して適用されるタイムゾーンを設定することも可能になりました。 |
||||
[[yii\i18n\Formatter::defaultTimeZone]] を設定して、データストレージに使用しているタイムゾーンに合せることが出来ます。 |
||||
|
||||
> Note|注意: タイムゾーンは世界中のさまざまな政府によって作られる規則に従うものであり、頻繁に変更されるものであるため、あなたのシステムにインストールされたタイムゾーンのデータベースが最新の情報を持っていない可能性が大いにあります。 |
||||
> タイムゾーンデータベースの更新についての詳細は、[ICU マニュアル](http://userguide.icu-project.org/datetime/timezone#TOC-Updating-the-Time-Zone-Data) で参照することが出来ます。 |
||||
> [PHP 環境を国際化のために設定する](tutorial-i18n.md#setup-environment) も参照してください。 |
||||
|
||||
|
||||
数値をフォーマットする <a name="numbers"></a> |
||||
---------------------- |
||||
|
||||
数値をフォーマットするために、フォーマッタクラスは次のメソッドを提供しています。 |
||||
|
||||
- [[yii\i18n\Formatter::asInteger()|integer]] - 値は整数としてフォーマットされます。例えば `42`。 |
||||
- [[yii\i18n\Formatter::asDecimal()|decimal]] - 値は小数点と三桁ごとの区切りを使って十進数としてフォーマットされます。例えば `2,542.123` または `2.542,123`。 |
||||
- [[yii\i18n\Formatter::asPercent()|percent]] - 値は百分率としてフォーマットされます。例えば `42%`。 |
||||
- [[yii\i18n\Formatter::asScientific()|scientific]] - 値は科学記法による数値としてフォーマットされます。例えば `4.2E4`。 |
||||
- [[yii\i18n\Formatter::asCurrency()|currency]] - 値は通貨の値としてフォーマットされます。例えば `£420.00`。 |
||||
- [[yii\i18n\Formatter::asSize()|size]] - バイト数である値が人間にとって読みやすいサイズとしてフォーマットされます。例えば `410 キビバイト`。 |
||||
- [[yii\i18n\Formatter::asShortSize()|shortSize]] - [[yii\i18n\Formatter::asSize()|size]] の短いバージョンです。例えば `410 KiB`。 |
||||
|
||||
数値のフォーマットに使われる書式は、デフォルトではロケールに従って設定される [[yii\i18n\Formatter::decimalSeparator|decimalSeparator]] と [[yii\i18n\Formatter::thousandSeparator|thousandSeparator]] を使って調整することが出来ます。 |
||||
|
||||
更に高度な設定のためには、[[yii\i18n\Formatter::numberFormatterOptions]] と [[yii\i18n\Formatter::numberFormatterTextOptions]] を使って、内部的に使用される [NumberFormatter クラス](http://php.net/manual/ja/class.numberformatter.php) を構成することが出来ます。 |
||||
|
||||
例えば、小数部の最大桁数と最小桁数を調整するためには、次のように [[yii\i18n\Formatter::numberFormatterOptions]] プロパティを構成します。 |
||||
|
||||
```php |
||||
'numberFormatterOptions' => [ |
||||
NumberFormatter::MIN_FRACTION_DIGITS => 0, |
||||
NumberFormatter::MAX_FRACTION_DIGITS => 2, |
||||
] |
||||
``` |
||||
|
||||
その他のフォーマッタ <a name="other"></a> |
||||
-------------------- |
||||
|
||||
日付、時刻、そして、数値の他にも、Yii はさまざまな状況で使える一連のフォーマッタを提供しています。 |
||||
|
||||
- [[yii\i18n\Formatter::asRaw()|raw]] - 値はそのまま出力されます。`null` 値が [[nullDisplay]] を使ってフォーマットされる以外は、何の効果のない擬似フォーマッタです。 |
||||
- [[yii\i18n\Formatter::asText()|text]] - 値は HTML エンコードされます。 |
||||
これは [GridView DataColumn](output-data-widgets.md#data-column) で使われるデフォルトの形式です。 |
||||
- [[yii\i18n\Formatter::asNtext()|ntext]] - 値は HTML エンコードされ、改行文字が強制改行に変換された平文テキストとしてフォーマットされ、ます。 |
||||
- [[yii\i18n\Formatter::asParagraphs()|paragraphs]] - 値は HTML エンコードされ、`<p>` タグに囲まれた段落としてフォーマットされます。 |
||||
- [[yii\i18n\Formatter::asHtml()|html]] - 値は XSS 攻撃を避けるために [[HtmlPurifier]] を使って浄化されます。 |
||||
`['html', ['Attr.AllowedFrameTargets' => ['_blank']]]` のような追加のオプションを渡すことが出来ます。 |
||||
- [[yii\i18n\Formatter::asEmail()|email]] - 値は `mailto` リンクとしてフォーマットされます。 |
||||
- [[yii\i18n\Formatter::asImage()|image]] - 値は `image` タグとしてフォーマットされます。 |
||||
- [[yii\i18n\Formatter::asUrl()|url]] - 値はハイパーリンクとしてフォーマットされます。 |
||||
- [[yii\i18n\Formatter::asBoolean()|boolean]] - 値は真偽値としてフォーマットされます。 |
||||
デフォルトでは、`true` は `Yes`、`false` は `No` とレンダリングされ、現在のアプリケーションの言語に翻訳されます。 |
||||
この振る舞いは [[yii\i18n\Formatter::booleanFormat]] プロパティを構成して調整できます。 |
||||
|
||||
`null` 値 <a name="null-values"></a> |
||||
--------- |
||||
|
||||
PHP において `null` である値に対して、フォーマッタクラスは空文字ではなくプレースホルダを表示します。 |
||||
`null` のプレースホルダは、デフォルトでは `(not set)` であり、それが現在のアプリケーションの言語に翻訳されます。 |
||||
[[yii\i18n\Formatter::nullDisplay|nullDisplay]] プロパティを構成して、カスタムのプレースホルダを設定することが出来ます。 |
||||
`null` 値の特別な扱いをしたくない場合は、[[yii\i18n\Formatter::nullDisplay|nullDisplay]] を `null` に設定することが出来ます。 |
@ -0,0 +1,6 @@
|
||||
ページネーション |
||||
================ |
||||
|
||||
> Note|注意: この節はまだ執筆中です。 |
||||
> |
||||
> まだ内容がありません。 |
@ -0,0 +1,6 @@
|
||||
並べ替え |
||||
======== |
||||
|
||||
> Note|注意: この節はまだ執筆中です。 |
||||
> |
||||
> まだ内容がありません。 |
@ -0,0 +1,102 @@
|
||||
テーマ |
||||
====== |
||||
|
||||
> Note|注意: この節はまだ執筆中です。 |
||||
|
||||
テーマとは、あるディレクトリの下に集められたビューとレイアウトのファイルです。 |
||||
テーマの各ファイルが、アプリケーションの対応するファイルをレンダリングの際にオーバーライドします。 |
||||
一つのアプリケーションは複数のテーマを使用することが可能で、それぞれのテーマはまったく異なるユーザ体験を提供することが出来ます。 |
||||
いつでも一つのテーマだけがアクティブになり得ます。 |
||||
|
||||
> Note|注意: ビューはアプリケーションの固有性が強いものですので、通常は、テーマを再配布可能なものとして作ることはしません。 |
||||
カスタマイズしたルックアンドフィールを再配布したい場合は、テーマの代りに、[アセットバンドル](structure-assets.md) の形で CSS と JavaScript のファイルを再配布することを検討してください。 |
||||
|
||||
テーマを構成する |
||||
---------------- |
||||
|
||||
テーマの構成情報は、アプリケーションの `view` コンポーネントを通じて指定します。 |
||||
`basic application` のビューに対して働くテーマをセットアップするためには、アプリケーションの構成情報ファイルに以下のように記述しなければなりません。 |
||||
|
||||
```php |
||||
'components' => [ |
||||
'view' => [ |
||||
'theme' => [ |
||||
'pathMap' => ['@app/views' => '@app/themes/basic'], |
||||
'baseUrl' => '@web/themes/basic', |
||||
], |
||||
], |
||||
], |
||||
``` |
||||
|
||||
上記においては、`pathMap` が元のパスからテーマのパスへの割り付けを定義し、`baseUrl` がテーマのファイルによって参照されるリソースのベース URL を定義しています。 |
||||
|
||||
私たちの例では、`pathMap` は `['@app/views' => '@app/themes/basic']` です。 |
||||
これは、`@app/views` の全てのビューは、最初に `@app/themes/basic` の下で探され、そのテーマのディレクトリにビューが存在していれば、それが元のビューの代りに使われる、ということを意味します。 |
||||
|
||||
例えば、上記の構成においては、ビューファイル `@app/views/site/index.php` のテーマ版は `@app/themes/basic/site/index.php` になります。 |
||||
基本的には、`@app/views/site/index.php` の `@app/views` を `@app/themes/basic` に置き換えるわけです。 |
||||
|
||||
ランタイムにおいてテーマを構成するためには、ビューをレンダリングする前に次のコードを使用することが出来ます。 |
||||
典型的には、コントローラの中に次のコードを置きます。 |
||||
|
||||
```php |
||||
$this->getView()->theme = Yii::createObject([ |
||||
'class' => '\yii\base\Theme', |
||||
'pathMap' => ['@app/views' => '@app/themes/basic'], |
||||
'baseUrl' => '@web/themes/basic', |
||||
]); |
||||
``` |
||||
|
||||
### モジュールにテーマを適用する |
||||
|
||||
モジュールにテーマを適用するためには、`pathMap` を次のようなものにすることが出来ます。 |
||||
|
||||
```php |
||||
'components' => [ |
||||
'view' => [ |
||||
'theme' => [ |
||||
'pathMap' => [ |
||||
'@app/views' => '@app/themes/basic', |
||||
'@app/modules' => '@app/themes/basic/modules', // <-- !!! |
||||
], |
||||
], |
||||
], |
||||
], |
||||
``` |
||||
|
||||
これによって、`@app/modules/blog/views/comment/index.php` に `@app/themes/basic/modules/blog/views/comment/index.php` というテーマを適用することが出来ます。 |
||||
|
||||
### ウィジェットにテーマを適用する |
||||
|
||||
`@app/widgets/currency/views/index.php` に配置されているウィジェットのビューにテーマを適用するためには、ビューコンポーネントのテーマに、次のような構成情報を設定する必要があります。 |
||||
|
||||
```php |
||||
'components' => [ |
||||
'view' => [ |
||||
'theme' => [ |
||||
'pathMap' => ['@app/widgets' => '@app/themes/basic/widgets'], |
||||
], |
||||
], |
||||
], |
||||
``` |
||||
|
||||
上記の構成によって、`@app/widgets/currency/index.php` ビューのテーマ版を `@app/themes/basic/widgets/currency/index.php` として作成することが出来るようになります。 |
||||
|
||||
複数のパスを使う |
||||
---------------- |
||||
|
||||
一つのパスを複数のテーマパスに割り付けることが出来ます。例えば、 |
||||
|
||||
```php |
||||
'pathMap' => [ |
||||
'@app/views' => [ |
||||
'@app/themes/christmas', |
||||
'@app/themes/basic', |
||||
], |
||||
] |
||||
``` |
||||
|
||||
この場合、最初に `@app/themes/christmas/site/index.php` というビューファイルが探され、それが見つからない場合は、次に `@app/themes/basic/site/index.php` が探されます。 |
||||
そして、そこにもビューがない場合は、アプリケーションのビューが使用されます。 |
||||
|
||||
この機能は、いくつかのビューを一時的または条件的にオーバーライドしたい場合に、特に役立ちます。 |
@ -0,0 +1,6 @@
|
||||
Auth クライアント |
||||
================= |
||||
|
||||
> Note|注意: この節はまだ執筆中です。 |
||||
> |
||||
> まだ内容がありません。 |
@ -0,0 +1,92 @@
|
||||
認証 |
||||
==== |
||||
|
||||
> Note|注意: この節はまだ執筆中です。 |
||||
|
||||
認証はユーザが誰であるかを確認する行為であり、ログインプロセスの基礎となるものです。 |
||||
典型的には、認証は、識別子 (ユーザ名またはメールアドレス) とパスワードの組み合わせを使用します。 |
||||
ユーザはこれらの値をフォームを通じて送信し、アプリケーションは送信された情報を以前に (例えば、ユーザ登録時に) 保存された情報と比較します。 |
||||
|
||||
Yii では、このプロセス全体が半自動的に実行されます。 |
||||
開発者に残されているのは、認証システムにおいて最も重要なクラスである [[yii\web\IdentityInterface]] を実装することだけです。 |
||||
典型的には、`IdentityInterface` の実装は `User` モデルを使って達成されます。 |
||||
|
||||
十分な機能を有する認証の実例を [アドバンストアプリケーションテンプレート](tutorial-advanced-app.md) の中に見出すことが出来ます。 |
||||
下記にインターフェイスのメソッドだけをリストします。 |
||||
|
||||
```php |
||||
class User extends ActiveRecord implements IdentityInterface |
||||
{ |
||||
// ... |
||||
|
||||
/** |
||||
* 与えられた ID によって識別子を探す |
||||
* |
||||
* @param string|integer $id 探すための ID |
||||
* @return IdentityInterface|null 与えられた ID に合致する識別子オブジェクト |
||||
*/ |
||||
public static function findIdentity($id) |
||||
{ |
||||
return static::findOne($id); |
||||
} |
||||
|
||||
/** |
||||
* 与えられたトークンによって識別子を探す |
||||
* |
||||
* @param string $token 探すためのトークン |
||||
* @return IdentityInterface|null 与えられたトークンに合致する識別子オブジェクト |
||||
*/ |
||||
public static function findIdentityByAccessToken($token, $type = null) |
||||
{ |
||||
return static::findOne(['access_token' => $token]); |
||||
} |
||||
|
||||
/** |
||||
* @return int|string 現在のユーザの ID |
||||
*/ |
||||
public function getId() |
||||
{ |
||||
return $this->id; |
||||
} |
||||
|
||||
/** |
||||
* @return string 現在のユーザの認証キー |
||||
*/ |
||||
public function getAuthKey() |
||||
{ |
||||
return $this->auth_key; |
||||
} |
||||
|
||||
/** |
||||
* @param string $authKey |
||||
* @return boolean 認証キーが現在のユーザに対して有効か否か |
||||
*/ |
||||
public function validateAuthKey($authKey) |
||||
{ |
||||
return $this->getAuthKey() === $authKey; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
概要を述べたメソッドのうち、二つは単純なものです。 |
||||
`findIdentity` は ID の値を受け取って、その ID と関連付けられたモデルのインスタンスを返します。 |
||||
`getId` メソッドは ID そのものを返します。 |
||||
その他のメソッドのうち、二つのもの - `getAuthKey` と `validateAuthKey` - は、「次回から自動ログイン ("remember me")」のクッキーに対して追加のセキュリティを提供するために使われます。 |
||||
`getAuthKey` メソッドは全てのユーザに対してユニークな文字列を返さなければなりません。 |
||||
`Yii::$app->getSecurity()->generateRandomString()` を使うと、信頼性の高い方法でユニークな文字列を生成することが出来ます。 |
||||
これをユーザのレコードの一部として保存しておくのが良いアイデアです。 |
||||
|
||||
```php |
||||
public function beforeSave($insert) |
||||
{ |
||||
if (parent::beforeSave($insert)) { |
||||
if ($this->isNewRecord) { |
||||
$this->auth_key = Yii::$app->getSecurity()->generateRandomString(); |
||||
} |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
``` |
||||
|
||||
`validateAuthKey` メソッドでは、パラメータとして渡された `$authKey` 変数 (これ自体はクッキーから読み出されます) をデータベースから読み出された値と比較する必要があるだけです。 |
@ -0,0 +1,482 @@
|
||||
権限付与 |
||||
======== |
||||
|
||||
> Note|注意: この節はまだ執筆中です。 |
||||
|
||||
権限付与は、ユーザが何かをするのに十分な許可を得ているか否かを確認するプロセスです。 |
||||
Yii は二つの権限付与の方法を提供しています。すなわち、アクセス制御フィルタ (ACF) と、ロールベースアクセス制御 (RBAC) です。 |
||||
|
||||
|
||||
アクセス制御フィルタ (ACF) |
||||
-------------------------- |
||||
|
||||
アクセス制御フィルタ (ACF) は、何らかの単純なアクセス制御だけを必要とするアプリケーションで使うのに最も適した、単純な権限付与の方法です。 |
||||
その名前が示すように、ACF は、コントローラまたはモジュールにビヘイビアとしてアタッチすることが出来るアクションフィルタです。 |
||||
ACF は一連の [[yii\filters\AccessControl::rules|アクセス規則]] をチェックして、現在のユーザがリクエストしたアクションにアクセスすることが出来るかどうかを確認します。 |
||||
|
||||
下記のコードは、[[yii\filters\AccessControl]] として実装された ACF の使い方を示すものです。 |
||||
|
||||
```php |
||||
use yii\filters\AccessControl; |
||||
|
||||
class SiteController extends Controller |
||||
{ |
||||
public function behaviors() |
||||
{ |
||||
return [ |
||||
'access' => [ |
||||
'class' => AccessControl::className(), |
||||
'only' => ['login', 'logout', 'signup'], |
||||
'rules' => [ |
||||
[ |
||||
'allow' => true, |
||||
'actions' => ['login', 'signup'], |
||||
'roles' => ['?'], |
||||
], |
||||
[ |
||||
'allow' => true, |
||||
'actions' => ['logout'], |
||||
'roles' => ['@'], |
||||
], |
||||
], |
||||
], |
||||
]; |
||||
} |
||||
// ... |
||||
} |
||||
``` |
||||
|
||||
上記のコードにおいて、ACF は `site` コントローラにビヘイビアとしてアタッチされています。 |
||||
これはアクションフィルタを使用する典型的な方法です。 |
||||
`only` オプションは、ACF が `login`、`logout`、`signup` のアクションにのみ適用されるべきであることを指定しています。 |
||||
`rules` オプションは [[yii\filters\AccessRule|アクセス規則]] を指定するものであり、以下のように読むことが出来ます。 |
||||
|
||||
- 全てのゲストユーザ (まだ認証されていないユーザ) に、'login' と 'singup' のアクションにアクセスすることを許可します。 |
||||
`roles` オプションに疑問符 `?` が含まれていますが、これは「ゲスト」として認識される特殊なトークンです。 |
||||
- 認証されたユーザに、'logout' アクションにアクセスすることを許可します。 |
||||
`@` という文字はもう一つの特殊なトークンで、認証されたユーザとして認識されるものです。 |
||||
|
||||
ACF が権限のチェックを実行するときには、規則を一つずつ上から下へ、適用されるものを見つけるまで調べます。 |
||||
そして、適用される規則の `allow` の値が、ユーザが権限を有するか否かを判断するのに使われます。 |
||||
適用される規則が一つもなかった場合は、ユーザが権限をもたないことを意味し、ACF はアクションの継続を中止します。 |
||||
|
||||
デフォルトでは、ユーザが現在のアクションにアクセスする権限を持っていないと判定した場合は、ACF は以下のことだけを行います。 |
||||
|
||||
* ユーザがゲストである場合は、[[yii\web\User::loginRequired()]] を呼び出します。 |
||||
このメソッドで、ブラウザをログインページにリダイレクトすることが出来ます。 |
||||
* ユーザが既に認証されている場合は、[[yii\web\ForbiddenHttpException]] を投げます。 |
||||
|
||||
この動作は、[[yii\filters\AccessControl::denyCallback]] プロパティを構成することによって、カスタマイズすることが出来ます。 |
||||
|
||||
```php |
||||
[ |
||||
'class' => AccessControl::className(), |
||||
'denyCallback' => function ($rule, $action) { |
||||
throw new \Exception('このページにアクセスする権限がありません。'); |
||||
} |
||||
] |
||||
``` |
||||
|
||||
[[yii\filters\AccessRule|アクセス規則]] は多くのオプションをサポートしています。 |
||||
以下はサポートされているオプションの要約です。 |
||||
[[yii\filters\AccessRule]] を拡張して、あなた自身のカスタマイズしたアクセス規則のクラスを作ることも出来ます。 |
||||
|
||||
* [[yii\filters\AccessRule::allow|allow]]: これが「許可」の規則であるか、「禁止」の規則であるかを指定します。 |
||||
|
||||
* [[yii\filters\AccessRule::actions|actions]]: どのアクションにこの規則が適用されるかを指定します。 |
||||
これはアクション ID の配列でなければなりません。 |
||||
比較は大文字と小文字を区別します。 |
||||
このオプションが空であるか指定されていない場合は、規則が全てのアクションに適用されることを意味します。 |
||||
|
||||
* [[yii\filters\AccessRule::controllers|controllers]]: どのコントローラにこの規則が適用されるかを指定します。 |
||||
これはコントローラ ID の配列でなければなりません。 |
||||
比較は大文字と小文字を区別します。 |
||||
このオプションが空であるか指定されていない場合は、規則が全てのコントローラに適用されることを意味します。 |
||||
|
||||
* [[yii\filters\AccessRule::roles|roles]]: どのユーザロールにこの規則が適用されるかを指定します。 |
||||
二つの特別なロールが認識されます。 |
||||
これらは、[[yii\web\User::isGuest]] によって判断されます。 |
||||
|
||||
- `?`: ゲストユーザ (まだ認証されていないユーザ) を意味します。 |
||||
- `@`: 認証されたユーザを意味します。 |
||||
|
||||
その他のロール名を使う場合には、RBAC (次の節で説明します) が必要とされ、判断のために [[yii\web\User::can()]] が呼び出されます。 |
||||
このオプションが空であるか指定されていない場合は、規則が全てのロールに適用されることを意味します。 |
||||
|
||||
* [[yii\filters\AccessRule::ips|ips]]: どの [[yii\web\Request::userIP|クライアントの IP アドレス]] にこの規則が適用されるかを指定します。 |
||||
IP アドレスは、最後にワイルドカード `*` を含むことが出来て、同じプレフィクスを持つ IP アドレスに合致させることが出来ます。 |
||||
例えば、'192.168.*' は、'192.168.' のセグメントに属する全ての IP アドレスに合致します。 |
||||
このオプションが空であるか指定されていない場合は、規則が全ての IP アドレスに適用されることを意味します。 |
||||
|
||||
* [[yii\filters\AccessRule::verbs|verbs]]: どのリクエストメソッド (例えば、`GET` や `POST`) にこの規則が適用されるかを指定します。 |
||||
比較は大文字と小文字を区別しません。 |
||||
|
||||
* [[yii\filters\AccessRule::matchCallback|matchCallback]]: この規則が適用されるべきか否かを決定するために呼び出されるべき PHP コーラブルを指定します。 |
||||
|
||||
* [[yii\filters\AccessRule::denyCallback|denyCallback]]: この規則がアクセスを禁止する場合に呼び出されるべき PHP コーラブルを指定します。 |
||||
|
||||
下記は、`matchCallback` オプションを利用する方法を示す例です。 |
||||
このオプションによって、任意のアクセス制御ロジックを書くことが可能になります。 |
||||
|
||||
```php |
||||
use yii\filters\AccessControl; |
||||
|
||||
class SiteController extends Controller |
||||
{ |
||||
public function behaviors() |
||||
{ |
||||
return [ |
||||
'access' => [ |
||||
'class' => AccessControl::className(), |
||||
'only' => ['special-callback'], |
||||
'rules' => [ |
||||
[ |
||||
'actions' => ['special-callback'], |
||||
'allow' => true, |
||||
'matchCallback' => function ($rule, $action) { |
||||
return date('d-m') === '31-10'; |
||||
} |
||||
], |
||||
], |
||||
], |
||||
]; |
||||
} |
||||
|
||||
// matchCallback が呼ばれる。このページは毎年10月31日だけアクセス出来ます。 |
||||
public function actionSpecialCallback() |
||||
{ |
||||
return $this->render('happy-halloween'); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
|
||||
ロールベースアクセス制御 (RBAC) |
||||
--------------------------------------- |
||||
|
||||
ロールベースアクセス制御 (RBAC) は、単純でありながら強力な集中型のアクセス制御を提供します。 |
||||
RBAC と他のもっと伝統的なアクセス制御スキーマとの比較に関する詳細については、[Wiki 記事](http://ja.wikipedia.org/wiki/%E3%83%AD%E3%83%BC%E3%83%AB%E3%83%99%E3%83%BC%E3%82%B9%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E5%88%B6%E5%BE%A1) を参照してください。 |
||||
|
||||
Yii は、[NIST RBAC モデル](http://csrc.nist.gov/rbac/sandhu-ferraiolo-kuhn-00.pdf) に従って、一般的階層型 RBAC を実装しています。 |
||||
RBAC の機能は、[[yii\rbac\ManagerInterface|authManager]] [アプリケーションコンポーネント](structure-application-components.md) を通じて提供されます。 |
||||
|
||||
RBAC を使用することには、二つの作業が含まれます。 |
||||
最初の作業は、RBAC 権限付与データを作り上げることであり、第二の作業は、権限付与データを使って必要とされる場所でアクセスチェックを実行することです。 |
||||
|
||||
説明を容易にするために、まず、いくつかの基本的な RBAC の概念を導入します。 |
||||
|
||||
|
||||
### 基本的な概念 |
||||
|
||||
ロール (役割) は、*許可* (例えば、記事を作成する、記事を更新するなど) のコレクションです。 |
||||
一つのロールを一人または複数のユーザに割り当てることが出来ます。 |
||||
ユーザが特定の許可を有しているか否かをチェックするためには、その許可を含むロールがユーザに割り当てられているか否かをチェックすればよいのです。 |
||||
|
||||
各ロールまたは許可に関連付けられた *規則* が存在することがあり得ます。 |
||||
規則とは、アクセスチェックの際に、対応するロールや許可が現在のユーザに適用されるか否かを決定するために実行されるコード断片のことです。 |
||||
例えば、「記事更新」の許可は、現在のユーザが記事の作成者であるかどうかをチェックする規則を持つことが出来ます。 |
||||
そして、アクセスチェックのときに、ユーザが記事の作成者でない場合は、彼/彼女は「記事更新」の許可を持っていないと見なすことが出来ます。 |
||||
|
||||
ロールおよび許可は、ともに、階層的に構成することが出来ます。 |
||||
具体的に言えば、一つのロールは他のロールと許可を含むことが出来、許可は他の許可を含むことが出来ます。 |
||||
Yii は、一般的な *半順序* 階層を実装していますが、これはその特殊形として *木* 階層を含むものです。 |
||||
ロールは許可を含むことが出来ますが、許可はロールを含むことが出来ません。 |
||||
|
||||
|
||||
### RBAC マネージャを構成する |
||||
|
||||
権限付与データを定義してアクセスチェックを実行する前に、[[yii\base\Application::authManager|authManager]] アプリケーションコンポーネントを構成する必要があります。 |
||||
Yii は二種類の権限付与マネージャを提供しています。すなわち、[[yii\rbac\PhpManager]] と [[yii\rbac\DbManager]] です。 |
||||
前者は権限付与データを保存するのに PHP スクリプトファイルを使いますが、後者は権限付与データをデータベースに保存します。 |
||||
あなたのアプリケーションが非常に動的なロールと許可の管理を必要とするのでなければ、前者を使うことを考慮するのが良いでしょう。 |
||||
|
||||
次のコードは、アプリケーションの構成情報で `authManager` を構成する方法を示すものです。 |
||||
|
||||
```php |
||||
return [ |
||||
// ... |
||||
'components' => [ |
||||
'authManager' => [ |
||||
'class' => 'yii\rbac\PhpManager', |
||||
], |
||||
// ... |
||||
], |
||||
]; |
||||
``` |
||||
|
||||
`authManager` は `\Yii::$app->authManager` によってアクセスすることが出来ます。 |
||||
|
||||
> Tip|ヒント: デフォルトでは、[[yii\rbac\PhpManager]] は RBAC データを `@app/rbac/` ディレクトリの下のファイルに保存します。 |
||||
権限の階層をオンラインで変更する必要がある場合は、必ず、ウェブサーバのプロセスがこのディレクトリとその中の全てのファイルに対する書き込み権限を有するようにしてください。 |
||||
|
||||
|
||||
### 権限付与データを構築する |
||||
|
||||
権限付与データを構築する作業は、つまるところ、以下のタスクに他なりません。 |
||||
|
||||
- ロールと許可を定義する |
||||
- ロールと許可の関係を定義する |
||||
- 規則を定義する |
||||
- 規則をロールと許可に結び付ける |
||||
- ロールをユーザに割り当てる |
||||
|
||||
権限付与に要求される柔軟性の程度によって、上記のタスクのやりかたも異なってきます。 |
||||
|
||||
権限の階層が全く変化せず、決った数のユーザしか存在しない場合は、`authManager` が提供する API によって権限付与データを一回だけ初期設定する [コンソールコマンド](tutorial-console.md#create-command) を作ることが出来ます。 |
||||
|
||||
```php |
||||
<?php |
||||
namespace app\commands; |
||||
|
||||
use Yii; |
||||
use yii\console\Controller; |
||||
|
||||
class RbacController extends Controller |
||||
{ |
||||
public function actionInit() |
||||
{ |
||||
$auth = Yii::$app->authManager; |
||||
|
||||
// "createPost" という許可を追加 |
||||
$createPost = $auth->createPermission('createPost'); |
||||
$createPost->description = '記事を投稿'; |
||||
$auth->add($createPost); |
||||
|
||||
// "updatePost" という許可を追加 |
||||
$updatePost = $auth->createPermission('updatePost'); |
||||
$updatePost->description = '記事を更新'; |
||||
$auth->add($updatePost); |
||||
|
||||
// "author" というロールを追加し、このロールに "createPost" 許可を与える |
||||
$author = $auth->createRole('author'); |
||||
$auth->add($author); |
||||
$auth->addChild($author, $createPost); |
||||
|
||||
// "admin" というロールを追加し、このロールに "updatePost" 許可を与える |
||||
// 同時に、"author" ロールの持つ許可も与える |
||||
$admin = $auth->createRole('admin'); |
||||
$auth->add($admin); |
||||
$auth->addChild($admin, $updatePost); |
||||
$auth->addChild($admin, $author); |
||||
|
||||
// ロールをユーザに割り当てる。1 と 2 は IdentityInterface::getId() によって返される ID |
||||
// 通常はユーザモデルの中で実装する |
||||
$auth->assign($author, 2); |
||||
$auth->assign($admin, 1); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
`yii rbac/init` によってコマンドを実行した後には、次の権限階層が得られます。 |
||||
|
||||
![単純な RBAC 階層](images/rbac-hierarchy-1.png "単純な RBAC 階層") |
||||
|
||||
投稿者 (author) は記事を投稿することが出来、管理者 (admin) は記事を更新することに加えて投稿者が出来る全てのことが出来ます。 |
||||
|
||||
あなたのアプリケーションがユーザ登録を許している場合は、新しく登録されたユーザに一度ロールを割り当てる必要があります。 |
||||
例えば、アドバンストアプリケーションテンプレートにおいては、登録したユーザの全てを「投稿者」にするために、`frontend\models\SignupForm::signup()` を次のように修正しなければなりません。 |
||||
|
||||
```php |
||||
public function signup() |
||||
{ |
||||
if ($this->validate()) { |
||||
$user = new User(); |
||||
$user->username = $this->username; |
||||
$user->email = $this->email; |
||||
$user->setPassword($this->password); |
||||
$user->generateAuthKey(); |
||||
$user->save(false); |
||||
|
||||
// 次の三行が追加されたものです |
||||
$auth = Yii::$app->authManager; |
||||
$authorRole = $auth->getRole('author'); |
||||
$auth->assign($authorRole, $user->getId()); |
||||
|
||||
return $user; |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
``` |
||||
|
||||
動的に更新される権限付与データを持つ複雑なアクセス制御を必要とするアプリケーションについては、`authManager` が提供する API を使って、特別なユーザインタフェイス (つまり、管理パネル) を開発する必要があるでしょう。 |
||||
|
||||
|
||||
### 規則を使う |
||||
|
||||
既に述べたように、規則がロールと許可に制約を追加します。 |
||||
規則は [[yii\rbac\Rule]] を拡張したクラスであり、[[yii\rbac\Rule::execute()|execute()]] メソッドを実装しなければなりません。 |
||||
前に作った権限階層においては、投稿者は自分自身の記事を編集することが出来ないものでした。これを修正しましょう。 |
||||
最初に、ユーザが記事の投稿者であることを確認する規則が必要です。 |
||||
|
||||
```php |
||||
namespace app\rbac; |
||||
|
||||
use yii\rbac\Rule; |
||||
|
||||
/** |
||||
* authorID がパラメータで渡されたユーザと一致するかチェックする |
||||
*/ |
||||
class AuthorRule extends Rule |
||||
{ |
||||
public $name = 'isAuthor'; |
||||
|
||||
/** |
||||
* @param string|integer $user ユーザ ID |
||||
* @param Item $item この規則が関連付けられているロールまたは許可 |
||||
* @param array $params ManagerInterface::checkAccess() に渡されたパラメータ |
||||
* @return boolean 関連付けられたロールまたは許可を認めるか否かを示す値 |
||||
*/ |
||||
public function execute($user, $item, $params) |
||||
{ |
||||
return isset($params['post']) ? $params['post']->createdBy == $user : false; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
上の規則は、`post` が `$user` によって作成されたかどうかをチェックします。 |
||||
次に、前に使ったコマンドの中で、`updateOwnPost` という特別な許可を作成します。 |
||||
|
||||
```php |
||||
$auth = Yii::$app->authManager; |
||||
|
||||
// 規則を追加する |
||||
$rule = new \app\rbac\AuthorRule; |
||||
$auth->add($rule); |
||||
|
||||
// "updateOwnPost" という許可を作成し、それに規則を関連付ける |
||||
$updateOwnPost = $auth->createPermission('updateOwnPost'); |
||||
$updateOwnPost->description = '自分の記事を更新'; |
||||
$updateOwnPost->ruleName = $rule->name; |
||||
$auth->add($updateOwnPost); |
||||
|
||||
// "updateOwnPost" は "updatePost" から使われる |
||||
$auth->addChild($updateOwnPost, $updatePost); |
||||
|
||||
// "author" に自分の記事を更新することを許可する |
||||
$auth->addChild($author, $updateOwnPost); |
||||
``` |
||||
|
||||
これで、次のような権限階層になります。 |
||||
|
||||
![規則を持つ RBAC 階層](images/rbac-hierarchy-2.png "規則を持つ RBAC 階層") |
||||
|
||||
### アクセスチェック |
||||
|
||||
権限付与データが準備できてしまえば、アクセスチェックは [[yii\rbac\ManagerInterface::checkAccess()]] メソッドを呼ぶだけの簡単な仕事です。 |
||||
たいていのアクセスチェックは現在のユーザに関するものですから、Yii は、便利なように、[[yii\web\User::can()]] というショートカットメソッドを提供しています。 |
||||
これは、次のようにして使うことが出来ます。 |
||||
|
||||
```php |
||||
if (\Yii::$app->user->can('createPost')) { |
||||
// 記事を作成する |
||||
} |
||||
``` |
||||
|
||||
現在のユーザが ID=1 である Jane であるとすると、`createPost` からスタートして `Jane` まで到達しようと試みます。 |
||||
|
||||
![アクセスチェック](images/rbac-access-check-1.png "アクセスチェック") |
||||
|
||||
ユーザが記事を更新することが出来るかどうかをチェックするためには、前に説明した `AuthorRule` によって要求される追加のパラメータを渡す必要があります。 |
||||
|
||||
```php |
||||
if (\Yii::$app->user->can('updatePost', ['post' => $post])) { |
||||
// 記事を更新する |
||||
} |
||||
``` |
||||
|
||||
現在のユーザが John であるとすると、次の経路をたどります。 |
||||
|
||||
![アクセスチェック](images/rbac-access-check-2.png "アクセスチェック") |
||||
|
||||
`updatePost` からスタートして、`updateOwnPost` を通過します。 |
||||
通過するためには、`AuthorRule` が `execute` メソッドで `true` を返さなければなりません。 |
||||
`execute` メソッドは `can` メソッドの呼び出しから `$params` を受け取りますので、その値は `['post' => $post]` です。 |
||||
すべて OK であれば、John に割り当てられている `author` に到達します。 |
||||
|
||||
Jane の場合は、彼女が管理者であるため、少し簡単になります。 |
||||
|
||||
![アクセスチェック](images/rbac-access-check-3.png "アクセスチェック") |
||||
|
||||
### デフォルトロールを使う |
||||
|
||||
デフォルトロールというのは、*全て* のユーザに *黙示的* に割り当てられるロールです。 |
||||
[[yii\rbac\ManagerInterface::assign()]] を呼び出す必要はなく、権限付与データはその割り当て情報を含みません。 |
||||
|
||||
デフォルトロールは、通常、そのロールが当該ユーザに適用されるかどうかを決定する規則と関連付けられます。 |
||||
|
||||
デフォルトロールは、たいていは、何らかのロールの割り当てを既に持っているアプリケーションにおいて使われます。 |
||||
例えば、アプリケーションによっては、ユーザのテーブルに "group" というカラムを持って、個々のユーザが属する特権グループを表している場合があります。 |
||||
それぞれの特権グループを RBAC ロールに対応付けることが出来るのであれば、デフォルトロールの機能を使って、それぞれのユーザに RBAC ロールを自動的に割り当てることが出来ます。 |
||||
どのようにすればこれが出来るのか、例を使って説明しましょう。 |
||||
|
||||
ユーザのテーブルに `group` というカラムがあって、1 は管理者グループ、2 は投稿者グループを示していると仮定しましょう。 |
||||
これら二つのグループの権限を表すために、それぞれ、`admin` と `author` という RBAC ロールを作ることにします。 |
||||
このとき、次のように RBAC データをセットアップすることが出来ます。 |
||||
|
||||
|
||||
```php |
||||
namespace app\rbac; |
||||
|
||||
use Yii; |
||||
use yii\rbac\Rule; |
||||
|
||||
/** |
||||
* ユーザのグループが合致するかどうかをチェックする |
||||
*/ |
||||
class UserGroupRule extends Rule |
||||
{ |
||||
public $name = 'userGroup'; |
||||
|
||||
public function execute($user, $item, $params) |
||||
{ |
||||
if (!Yii::$app->user->isGuest) { |
||||
$group = Yii::$app->user->identity->group; |
||||
if ($item->name === 'admin') { |
||||
return $group == 1; |
||||
} elseif ($item->name === 'author') { |
||||
return $group == 1 || $group == 2; |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
$auth = Yii::$app->authManager; |
||||
|
||||
$rule = new \app\rbac\UserGroupRule; |
||||
$auth->add($rule); |
||||
|
||||
$author = $auth->createRole('author'); |
||||
$author->ruleName = $rule->name; |
||||
$auth->add($author); |
||||
// ... $author の子として許可を追加 ... |
||||
|
||||
$admin = $auth->createRole('admin'); |
||||
$admin->ruleName = $rule->name; |
||||
$auth->add($admin); |
||||
$auth->addChild($admin, $author); |
||||
// ... $admin の子として許可を追加 ... |
||||
``` |
||||
|
||||
上記において、"author" が "admin" の子として追加されているため、規則クラスの `execute()` メソッドを実装する時には、この階層関係にも配慮しなければならないことに注意してください。 |
||||
このために、ロール名が "author" である場合には、`execute()` メソッドは、ユーザのグループが 1 または 2 である (ユーザが "admin" グループまたは "author" グループに属している) ときに true を返しています。 |
||||
|
||||
次に、`authManager` の構成情報で、この二つのロールを [[yii\rbac\BaseManager::$defaultRoles]] としてリストします。 |
||||
|
||||
```php |
||||
return [ |
||||
// ... |
||||
'components' => [ |
||||
'authManager' => [ |
||||
'class' => 'yii\rbac\PhpManager', |
||||
'defaultRoles' => ['admin', 'author'], |
||||
], |
||||
// ... |
||||
], |
||||
]; |
||||
``` |
||||
|
||||
このようにすると、アクセスチェックを実行すると、`admin` と `author` の両方のロールは、それらと関連付けられた規則を評価することによってチェックされるようになります。 |
||||
規則が true を返せば、そのロールが現在のユーザに適用されることになります。 |
||||
上述の規則の実装に基づいて言えば、ユーザの `group` の値が 1 であれば、`admin` ロールがユーザに適用され、`group` の値が 2 であれば `author` ロールが適用されるということを意味します。 |
@ -0,0 +1,163 @@
|
||||
セキュリティのベストプラクティス |
||||
================================ |
||||
|
||||
下記において、一般的なセキュリティの指針を復習し、Yii を使ってアプリケーションを開発するときに脅威を回避する方法を説明します。 |
||||
|
||||
基本的な指針 |
||||
------------ |
||||
|
||||
どのようなアプリケーションが開発されているかに関わらず、セキュリティに関しては二つの大きな指針が存在します。 |
||||
|
||||
1. 入力をフィルタする。 |
||||
2. 出力をエスケープする。 |
||||
|
||||
|
||||
### 入力をフィルタする |
||||
|
||||
入力をフィルタするとは、入力値は決して安全なものであると見なさず、取得した値が実際に許可さていれる値に含まれるか否かを常にチェックしなければならない、ということを意味します。 |
||||
つまり、並べ替えが三つのフィールド `title`、`created_at` および `status` によって実行され、フィールドの値がインプットによって提供されるものであることを知っている場合、取得した値を受信するその場でチェックする方が良い、ということです。 |
||||
基本的な PHP の形式では、次のようなコードになります。 |
||||
|
||||
```php |
||||
$sortBy = $_GET['sort']; |
||||
if (!in_array($sortBy, ['title', 'created_at', 'status'])) { |
||||
throw new Exception('Invalid sort value.'); |
||||
} |
||||
``` |
||||
|
||||
Yii においては、たいていの場合、同様のチェックを行うために [フォームのバリデーション](input-validation.md) を使うことになるでしょう。 |
||||
|
||||
|
||||
### 出力をエスケープする |
||||
|
||||
データを使用するコンテキストに応じて、出力をエスケープしなければなりません。 |
||||
つまり、HTML のコンテキストでは、`<` や `>` などの特殊な文字をエスケープしなければなりません。 |
||||
JavaScript や SQL のコンテキストでは、対象となる文字は別のセットになります。 |
||||
全てを手動でエスケープするのは間違いを生じやすいことですから、Yii は異なるコンテキストに応じたエスケープを実行するためのさまざまなツールを提供しています。 |
||||
|
||||
SQL インジェクションを回避する |
||||
------------------------------ |
||||
|
||||
SQL インジェクションは、次のように、エスケープされていない文字列を連結してクエリテキストを構築する場合に発生します。 |
||||
|
||||
```php |
||||
$username = $_GET['username']; |
||||
$sql = "SELECT * FROM user WHERE username = '$username'"; |
||||
``` |
||||
|
||||
正しいユーザ名を提供する代りに、攻撃者は `'; DROP TABLE user; --` のような文字列をあなたのアプリケーションに与えることが出来ます。 |
||||
結果として構築される SQL は次のようになります。 |
||||
|
||||
```sql |
||||
SELECT * FROM user WHERE username = ''; DROP TABLE user; --' |
||||
``` |
||||
|
||||
これは有効なクエリで、空のユーザ名を持つユーザを探してから、`user` テーブルを削除します。 |
||||
おそらく、ウェブサイトは破壊されて、データは失われることになります (定期的なバックアップは設定済みですよね、ね? )。 |
||||
|
||||
Yii においては、ほとんどのデータベースクエリは、PDO のプリペアドステートメントを適切に使用する [アクティブレコード](db-active-record.md) を経由して実行されます。 |
||||
プリペアドステートメントの場合は、上で説明したようなクエリの改竄は不可能です。 |
||||
|
||||
それでも、[生のクエリ](db-dao.md) や [クエリビルダ](db-query-builder.md) を必要とする場合はあります。 |
||||
その場合には、データを渡すための安全な方法を使わなければなりません。 |
||||
データをカラムの値として使う場合は、プリペアドステートメントを使うことが望まれます。 |
||||
|
||||
```php |
||||
// query builder |
||||
$userIDs = (new Query()) |
||||
->select('id') |
||||
->from('user') |
||||
->where('status=:status', [':status' => $status]) |
||||
->all(); |
||||
|
||||
// DAO |
||||
$userIDs = $connection |
||||
->createCommand('SELECT id FROM user where status=:status') |
||||
->bindValues([':status' => $status]) |
||||
->queryColumn(); |
||||
``` |
||||
|
||||
データがカラム名やテーブル名を指定するために使われる場合は、事前定義された一連の値だけを許可するのが最善の方法です。 |
||||
|
||||
```php |
||||
function actionList($orderBy = null) |
||||
{ |
||||
if (!in_array($orderBy, ['name', 'status'])) { |
||||
throw new BadRequestHttpException('名前とステータスだけを並べ替えに使うことが出来ます。') |
||||
} |
||||
|
||||
// ... |
||||
} |
||||
``` |
||||
|
||||
それが不可能な場合は、テーブル名とカラム名をエスケープしなければなりません。 |
||||
Yii はそういうエスケープのための特別な文法を持っており、それを使うと、サポートされている全てのデータベースに対して同じ方法でエスケープすることが出来ます。 |
||||
|
||||
```php |
||||
$sql = "SELECT COUNT([[$column]]) FROM {{table}}"; |
||||
$rowCount = $connection->createCommand($sql)->queryScalar(); |
||||
``` |
||||
|
||||
この文法の詳細は、[テーブルとカラムの名前を引用符で囲む](db-dao.md#quoting-table-and-column-names) で読むことが出来ます。 |
||||
|
||||
|
||||
XSS を回避する |
||||
-------------- |
||||
|
||||
XSS すなわちクロスサイトスクリプティングは、ブラウザに HTML を出力する際に、出力が適切にエスケープされていないと発生します。 |
||||
例えば、ユーザ名を入力できるフォームで `Alexander` の代りに `<script>alert('Hello!');</script>` と入力した場合、ユーザ名をエスケープせずに出力している全てのページでは、JavaScript `alert('Hello!');` が実行されて、ブラウザにアラートボックスがポップアップ表示されます。 |
||||
ウェブサイト次第では、そのようなスクリプトを使って、無害なアラートではなく、あなたの名前を使ってメッセージを送信したり、さらには銀行取引を実行したりすることが可能です。 |
||||
|
||||
XSS の回避は、Yii においてはとても簡単です。一般に、二つのケースがあります。 |
||||
|
||||
1. データを平文テキストとして出力したい。 |
||||
2. データを HTML として出力したい。 |
||||
|
||||
平文テキストしか必要でない場合は、エスケープは次のようにとても簡単です。 |
||||
|
||||
|
||||
```php |
||||
<?= \yii\helpers\Html::encode($username) ?> |
||||
``` |
||||
|
||||
HTML である場合は、HtmlPurifier から助けを得ることが出来ます。 |
||||
|
||||
```php |
||||
<?= \yii\helpers\HtmlPurifier::process($description) ?> |
||||
``` |
||||
|
||||
HtmlPurifier の処理は非常に重いので、キャッシュを追加することを検討してください。 |
||||
|
||||
CSRF を回避する |
||||
--------------- |
||||
|
||||
TBD: what's CSRF, how it works, intro |
||||
|
||||
1. HTTP の規格に従うこと、すなわち、GET はアプリケーションの状態を変更すべきではない。 |
||||
2. Yii の CSRF 保護を有効にしておくこと。 |
||||
|
||||
TBD: how CSRF protection works |
||||
|
||||
|
||||
ファイルの曝露を回避する |
||||
------------------------ |
||||
|
||||
デフォルトでは、サーバのウェブルートは、`index.php` がある `web` ディレクトリを指すように意図されています。 |
||||
共有ホスティング環境の場合、それをすることが出来ずに、全てのコード、構成情報、ログをサーバのウェブルートの下に置かなくてはならないことがあり得ます。 |
||||
|
||||
そういう場合には、`web` 以外の全てに対してアクセスを拒否することを忘れないでください。 |
||||
それも出来ない場合は、アプリケーションを別の場所でホストすることを検討してください。 |
||||
|
||||
実運用環境ではデバッグ情報とデバッグツールを無効にする |
||||
------------------------------------------------------ |
||||
|
||||
デバッグモードでは、Yii は極めて多くのエラー情報を出力します。これは確かに開発には役立つものです。 |
||||
しかし、実際の所、これらの饒舌なエラー情報は、攻撃者にとっても、データベース構造、構成情報の値、コードの断片などを曝露してくれる重宝なものです。 |
||||
実運用のアプリケーションにおいては、決して `index.php` の `YII_DEBUG` を `true` にして走らせてはいけません。 |
||||
|
||||
実運用環境では Gii を決して有効にしてはいけません。 |
||||
Gii を使うと、データベース構造とコードに関する情報を得ることが出来るだけでなく、コードを Gii によって生成したもので書き換えることすら出来てしまいます。 |
||||
|
||||
デバッグツールバーは本当に必要でない限り実運用環境では使用を避けるべきです。 |
||||
これはアプリケーションと構成情報の全ての詳細を曝露することが出来ます。 |
||||
どうしても必要な場合は、あなたの IP だけに適切にアクセス制限されていることを再度チェックしてください。 |
@ -0,0 +1,152 @@
|
||||
セキュリティ |
||||
============ |
||||
|
||||
> Note|注意: この節はまだ執筆中です。 |
||||
|
||||
十分なセキュリティは、どのようなアプリケーションであっても、健全さを保って成功するためには欠くことが出来ないものです。 |
||||
不幸なことに、理解が不足しているためか、実装の難易度が高すぎるためか、セキュリティのことになると手を抜く開発者がたくさんいます。 |
||||
Yii によって駆動されるアプリケーションを可能な限り安全にするために、Yii はいくつかの優秀な使いやすいセキュリティ機能を内蔵しています。 |
||||
|
||||
|
||||
ハッシュとパスワードの検証 |
||||
-------------------------- |
||||
|
||||
ほとんどの開発者はパスワードを平文テキストでは保存できないということを知っていますが、パスワードを `md5` や `sha1` でハッシュしてもまだ安全だと思っている開発者がたくさんいます。 |
||||
かつては、前述のハッシュアルゴリズムを使えば十分であった時もありましたが、現代のハードウェアをもってすれば、そのようなハッシュはブルートフォースアタックを使って非常に簡単に復元することが可能です。 |
||||
|
||||
最悪のシナリオ (アプリケーションに侵入された場合) であっても、ユーザのパスワードについて強化されたセキュリティを提供することが出来るように、ブルートフォースアタックに対する耐性が強いハッシュアルゴリズムを使う必要があります。 |
||||
現在、最善の選択は `bcrypt` です。 |
||||
PHP では、[crypt 関数](http://php.net/manual/ja/function.crypt.php) を使って `bcrypt` ハッシュを生成することが出来ます。 |
||||
Yii は `crypt` を使ってハッシュを安全に生成し検証することを容易にする二つのヘルパ関数を提供します。 |
||||
|
||||
ユーザが初めてパスワードを提供するとき (例えば、ユーザ登録の時) には、パスワードをハッシュする必要があります。 |
||||
|
||||
|
||||
```php |
||||
$hash = Yii::$app->getSecurity()->generatePasswordHash($password); |
||||
``` |
||||
|
||||
そして、ハッシュを対応するモデル属性と関連付けて、後で使用するためにデータベースに保存します。 |
||||
|
||||
ユーザがログインを試みたときは、送信されたパスワードは、前にハッシュされて保存されたパスワードと照合して検証されます。 |
||||
|
||||
|
||||
```php |
||||
if (Yii::$app->getSecurity()->validatePassword($password, $hash)) { |
||||
// よろしい、ユーザをログインさせる |
||||
} else { |
||||
// パスワードが違う |
||||
} |
||||
``` |
||||
|
||||
擬似乱数データを生成する |
||||
------------------------ |
||||
|
||||
擬似乱数データはさまざまな状況で役に立ちます。 |
||||
例えば、メール経由でパスワードをリセットするときは、トークンを生成してデータベースに保存し、それをユーザにメールで送信します。 |
||||
そして、ユーザはこのトークンを自分がアカウントの所有者であることの証拠として使用します。 |
||||
このトークンがユニークかつ推測困難なものであることは非常に重要なことです。 |
||||
さもなくば、攻撃者がトークンの値を推測してユーザのパスワードをリセットする可能性があります。 |
||||
|
||||
Yii のセキュリティヘルパは擬似乱数データの生成を単純な作業にしてくれます。 |
||||
|
||||
|
||||
```php |
||||
$key = Yii::$app->getSecurity()->generateRandomString(); |
||||
``` |
||||
|
||||
暗号論的に安全な乱数データを生成するためには、`openssl` 拡張をインストールしている必要があることに注意してください。 |
||||
|
||||
暗号化と復号化 |
||||
-------------- |
||||
|
||||
Yii は秘密鍵を使ってデータを暗号化/復号化することを可能にする便利なヘルパ関数を提供しています。 |
||||
データを暗号化関数に渡して、秘密鍵を持つ者だけが復号化することが出来るようにすることが出来ます。 |
||||
例えば、何らかの情報をデータベースに保存する必要があるけれども、(たとえアプリケーションのデータベースが第三者に漏洩した場合でも) 秘密鍵を持つユーザだけがそれを見ることが出来るようにする必要がある、という場合には次のようにします。 |
||||
|
||||
```php |
||||
// $data と $secretKey はフォームから取得する |
||||
$encryptedData = Yii::$app->getSecurity()->encryptByPassword($data, $secretKey); |
||||
// $encryptedData をデータベースに保存する |
||||
``` |
||||
|
||||
そして、後でユーザがデータを読みたいときは、次のようにします。 |
||||
|
||||
```php |
||||
// $secretKey はユーザ入力から取得、$encryptedData はデータベースから取得 |
||||
$data = Yii::$app->getSecurity()->decryptByPassword($encryptedData, $secretKey); |
||||
``` |
||||
|
||||
データの完全性を確認する |
||||
------------------------ |
||||
|
||||
データが第三者によって改竄されたり、更には何らかの形で毀損されたりしていないことを確認する必要がある、という場合があります。 |
||||
Yii は二つのヘルパ関数の形で、データの完全性を確認するための簡単な方法を提供しています。 |
||||
|
||||
秘密鍵とデータから生成されたハッシュをデータにプレフィクスします。 |
||||
|
||||
```php |
||||
// $secretKey はアプリケーションまたはユーザの秘密、$genuineData は信頼できるソースから取得 |
||||
$data = Yii::$app->getSecurity()->hashData($genuineData, $secretKey); |
||||
``` |
||||
|
||||
データの完全性が毀損されていないかチェックします。 |
||||
|
||||
```php |
||||
// $secretKey はアプリケーションまたはユーザの秘密、$data は信頼できないソースから取得 |
||||
$data = Yii::$app->getSecurity()->validateData($data, $secretKey); |
||||
``` |
||||
|
||||
|
||||
todo: XSS prevention, CSRF prevention, cookie protection, refer to 1.1 guide |
||||
|
||||
プロパティを設定することによって、CSRF バリデーションをコントローラ および/または アクション単位で無効にすることも出来ます。 |
||||
|
||||
```php |
||||
namespace app\controllers; |
||||
|
||||
use yii\web\Controller; |
||||
|
||||
class SiteController extends Controller |
||||
{ |
||||
public $enableCsrfValidation = false; |
||||
|
||||
public function actionIndex() |
||||
{ |
||||
// CSRF バリデーションはこのアクションおよびその他のアクションに適用されない |
||||
} |
||||
|
||||
} |
||||
``` |
||||
|
||||
特定のアクションに対して CSRF バリデーションを無効にするためには、次のようにします。 |
||||
|
||||
```php |
||||
namespace app\controllers; |
||||
|
||||
use yii\web\Controller; |
||||
|
||||
class SiteController extends Controller |
||||
{ |
||||
public function beforeAction($action) |
||||
{ |
||||
// ... ここで、何らかの条件に基づいて `$this->enableCsrfValidation` をセットする ... |
||||
// 親のメソッドを呼ぶ。プロパティが true なら、その中で CSRF がチェックされる。 |
||||
return parent::beforeAction($action); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
クッキーを安全にする |
||||
-------------------- |
||||
|
||||
- validation |
||||
- httpOnly is default |
||||
|
||||
参照 |
||||
---- |
||||
|
||||
以下も参照してください。 |
||||
|
||||
- [ビューのセキュリティ](structure-views.md#security) |
||||
|
@ -0,0 +1,483 @@
|
||||
Controllers (Controladores) |
||||
=========== |
||||
|
||||
Os controllers (controladores) fazem parte da arquitetura [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller). |
||||
São objetos de classes que estendem de [[yii\base\Controller]] e são responsáveis |
||||
pelo processamento das requisições e por gerar respostas. Em particular, após |
||||
assumir o controle de [applications](structure-applications.md), controllers |
||||
analisarão os dados de entradas obtidos pela requisição, passarão estes dados |
||||
para os [models](structure-models.md) (modelos), incluirão os resultados dos models |
||||
(modelos) nas [views](structure-views.md) (visões) e finalmente gerarão as respostas |
||||
de saída. |
||||
|
||||
|
||||
## Actions (Ações) <a name="actions"></a> |
||||
|
||||
Os controllers são compostos por unidades básicas chamadas *ações* que podem ser |
||||
tratados pelos usuários finais a fim de realizar a sua execução. |
||||
|
||||
No exemplo a seguir mostra um controller `post` com duas ações: `view` e `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, |
||||
]); |
||||
} |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Na ação `view` (definido pelo método `actionView()`), o primeiro código carrega o |
||||
[model](structure-models.md) conforme o ID solicitado; Se o model for devidamente |
||||
carregado, a ação irá exibi-lo utilizado a [view](structure-views.md) chamada de `view`. |
||||
Caso contrário, a ação lançará uma exceção. |
||||
|
||||
Na ação `create` (definido pelo método `actionCreate()`), o código é parecido. |
||||
Primeiro ele tenta popular o [model](structure-models.md) usando os dados da requisição |
||||
em seguida os salva. Se ambos forem bem sucedidos, a ação redirecionará o navegador |
||||
para a ação `view` com o novo ID criado pelo model. Caso contrário, a ação exibirá |
||||
a view `create` na qual os usuário poderão fornecer os dados necessários. |
||||
|
||||
|
||||
## Routes (Rotas) <a name="routes"></a> |
||||
|
||||
Os usuários finais abordarão as ações por meio de *rotas*. Uma rota é uma string composta |
||||
pelas seguintes partes: |
||||
|
||||
* um ID do módulo: serve apenas se o controller pertencer a um [módulo](structure-modules.md) que não seja da aplicação; |
||||
* um ID do controller: uma string que identifica exclusivamente o controller dentre todos os controllers da mesma aplicação (ou do mesmo módulo, caso o controller pertença a um módulo); |
||||
* um ID da ação: uma string que identifica exclusivamente uma ação dentre todas as ações de um mesmo controller. |
||||
|
||||
As rotas seguem o seguinte formato: |
||||
|
||||
``` |
||||
IDdoController/IDdoAction |
||||
``` |
||||
|
||||
ou o seguinte formato se o controller estiver em um módulo: |
||||
|
||||
```php |
||||
IDdoModule/IDdoController/IDdoAction |
||||
``` |
||||
|
||||
Portanto, se um usuário fizer uma requisição com a URL `http://hostname/index.php?r=site/index`, |
||||
a ação `index` do controller `site` será executada. Para mais detalhes sobre como |
||||
as ações são resolvidas pelas rotas, por favor consulte a seção [Roteamento e Criação de URL](runtime-routing.md). |
||||
|
||||
|
||||
## Criando Controllers <a name="creating-controllers"></a> |
||||
|
||||
Em [[yii\web\Application|aplicações Web]], os controllers devem estender de [[yii\web\Controller]] |
||||
ou de suas classes filhas. De forma semelhante, em [[yii\console\Application|aplicaçoes console]], |
||||
os controllers devem estender de [[yii\console\Controller]] ou de suas classes filhos. O código a seguir define um controller `site`: |
||||
|
||||
```php |
||||
namespace app\controllers; |
||||
|
||||
use yii\web\Controller; |
||||
|
||||
class SiteController extends Controller |
||||
{ |
||||
} |
||||
``` |
||||
|
||||
|
||||
### IDs dos Controllers <a name="controller-ids"></a> |
||||
|
||||
Normalmente, um controller é projetado para tratar as requisições relativos a |
||||
um determinado tipo de recurso. Por esta razão, os IDs dos controllers geralmente |
||||
são substantivos que referenciam-se ao tipo de recurso que será tratado. |
||||
Por exemplo, você pode usar o `article` como o ID do um controller para tratar |
||||
dados de artigos. |
||||
|
||||
Por padrão, os IDs dos controllers devem conter apenas esses caracteres: |
||||
letras inglesas em caixa baixa, números, underscores (underline), traços e barras. |
||||
Por exemplo, `article` e `post-comment` são ambos IDs de controllers válidos, |
||||
enquanto `article?`, `PostComment`, `admin\post` não são. |
||||
|
||||
Um ID de controller também pode conter um prefixo para o subdiretório. Por exemplo, |
||||
`admin/article` representa um controller `article` em um subdiretório `admin` sob |
||||
o [[yii\base\Application::controllerNamespace|namespace do controller]] |
||||
Os caracteres válidos para os prefixos de subdiretórios incluem: letras inglesas |
||||
em caixa alto ou caixa baixa, números, underscores (underline) e barras, onde as |
||||
barras são usadas para separar os níveis dos subdiretórios (por exemplo, `panels/admin`). |
||||
|
||||
|
||||
### Nomenclatura da Classe do Controller <a name="controller-class-naming"></a> |
||||
|
||||
Os nomes da classes dos controllers podem ser derivadas dos IDs dos controllers |
||||
de acordo com as seguintes regras: |
||||
|
||||
* Colocar em caixa alta a primeira letra de cada palavra separadas por traço. |
||||
Observe que se o ID do controller possuir barras, a regra é aplicada apenas na |
||||
parte após a última barra no ID. |
||||
* Remover os traços e substituir todas as barras por barras invertidas. |
||||
* Adicionar `Controller` como sufixo. |
||||
* E preceder ao [[yii\base\Application::controllerNamespace|namespace controller]]. |
||||
|
||||
Segue alguns exemplos, assumindo que o [[yii\base\Application::controllerNamespace|namespace do controller]] |
||||
tenha por padrão o valor `app\controllers`: |
||||
|
||||
* `article` deriva-se de `app\controllers\ArticleController`; |
||||
* `post-comment` deriva-se de `app\controllers\PostCommentController`; |
||||
* `admin/post-comment` deriva-se de `app\controllers\admin\PostCommentController`; |
||||
* `adminPanels/post-comment` deriva-se de `app\controllers\adminPanels\PostCommentController`. |
||||
|
||||
As classes dos controllers devem ser [autoloadable](concept-autoloading.md). |
||||
Por esta razão, nos exemplos anteriores, o controller `article` deve ser salvo |
||||
no arquivo cuja [alias](concept-aliases.md) é `@app/controllers/ArticleController.php`; |
||||
enquanto o controller `admin/post2-comment` deve ser salvo no `@app/controllers/admin/Post2CommentController.php`. |
||||
|
||||
> Informação: No último exemplo `admin/post2-comment`, mostra como você pode colocar |
||||
um controller em um subdiretório do [[yii\base\Application::controllerNamespace|namespace controller]]. Isto é útil quando você quiser organizar seus controllers em diversas |
||||
categorias e não quiser usar [módulos](structure-modules.md). |
||||
|
||||
|
||||
### Mapeando Controllers <a name="controller-map"></a> |
||||
|
||||
Você pode configurar um [[yii\base\Application::controllerMap|mapeamento de controllers]] para superar as barreiras impostas pelos IDs de controllers e pelos nomes de classes |
||||
descritos acima. Isto é útil principalmente quando quiser esconder alguns controllers |
||||
de terceiros na qual você não tem controle sobre seus nomes de classes. |
||||
|
||||
Você pode configurar o [[yii\base\Application::controllerMap|mapeamento de controllers]] |
||||
na [configuração da aplicação](structure-applications.md#application-configurations) como o seguinte exemplo: |
||||
|
||||
```php |
||||
[ |
||||
'controllerMap' => [ |
||||
// declara o controller "account" usando um nome de classe |
||||
'account' => 'app\controllers\UserController', |
||||
|
||||
// declara o controller "article" usando uma configuração em array |
||||
'article' => [ |
||||
'class' => 'app\controllers\PostController', |
||||
'enableCsrfValidation' => false, |
||||
], |
||||
], |
||||
] |
||||
``` |
||||
|
||||
|
||||
### Controller Padrão <a name="default-controller"></a> |
||||
|
||||
Cada aplicação tem um controller padrão que é especificado pela propriedade [[yii\base\Application::defaultRoute]]. |
||||
Quando uma requisição não especificar uma [rota](#id-da-rota), será utilizada a |
||||
rota especificada pela propriedade. |
||||
Para as [[yii\web\Application|aplicações Web]], este valor é `'site'`, enquanto |
||||
para as [[yii\console\Application|aplicações console]] é `help`. Portanto, se uma |
||||
URL `http://hostname/index.php` for usada, significa que o controller `site` será |
||||
usado nesta requisição. |
||||
|
||||
Você pode alterar o controller padrão como a seguinte [configuração da aplicação](structure-applications.md#application-configurations): |
||||
|
||||
```php |
||||
[ |
||||
'defaultRoute' => 'main', |
||||
] |
||||
``` |
||||
|
||||
|
||||
## Criando Ações <a name="creating-actions"></a> |
||||
|
||||
Criar ações pode ser tão simples como a definição dos chamados *métodos de ação* |
||||
em uma classe controller. Um método de ação é um método *público* cujo nome inicia |
||||
com a palavra `action`. O valor de retorno representa os dados de resposta a serem |
||||
enviados aos usuário finais. O código a seguir define duas ações, `index` e `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 'Hello World'; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
|
||||
### IDs das Ações <a name="action-ids"></a> |
||||
|
||||
Uma ação muitas vezes é projetada para realizar uma manipulação em particular sobre |
||||
um recurso. Por esta razão, os IDs das ações geralmente são verbos, tais como `view`, `update`, etc. |
||||
|
||||
Por padrão, os IDs das ações devem conter apenas esses caracteres: letras inglesas |
||||
em caixa baixa, números, underscores (underline) e traços. Os traços em um ID da |
||||
ação são usados para separar palavras. Por exemplo, `view`, `update2`, `comment-post` |
||||
todos são IDs válidos, enquanto `view?`, `Update` não são. |
||||
|
||||
Você pode criar ações de duas maneiras: ações inline (em sequência) e |
||||
ações standalone (autônomas). Uma ação inline é definida pelo método |
||||
de uma classe controller, enquanto uma ação standalone é uma classe que estende de |
||||
[[yii\base\Action]] ou de suas classes filhas. As ações inline exigem menos esforço |
||||
para serem criadas e muitas vezes as preferidas quando não se tem a intenção de |
||||
reutilizar estas ações. Ações standalone, por outro lado, são criados principalmente |
||||
para serem utilizados em diferentes controllers ou para serem distribuídos como |
||||
[extensions](structure-extensions.md). |
||||
|
||||
|
||||
### Ações Inline <a name="inline-actions"></a> |
||||
|
||||
As ações inline referem-se a os chamados métodos de ação, que foram descritos anteriormente. |
||||
|
||||
Os nomes dos métodos de ações são derivadas dos IDs das ações de acordo com os |
||||
seguintes critérios: |
||||
|
||||
* Colocar em caixa alta a primeira letra de cada palavra do ID da ação; |
||||
* Remover os traços; |
||||
* Adicionar o prefixo `action`. |
||||
|
||||
Por exemplo, `index` torna-se `actionIndex` e `hello-world` torna-se `actionHelloWorld`. |
||||
|
||||
> Observação: Os nomes dos métodos de ações são *case-sensitive*. Se você tiver |
||||
um método chamado `ActionIndex`, não será considerado como um método de ação e |
||||
como resultado, o pedido para a ação `index` lançará uma exceção. Observe também |
||||
que os métodos de ações devem ser públicas. Um método privado ou protegido NÃO |
||||
será definido como ação inline. |
||||
|
||||
As ações inline normalmente são as mais utilizadas pois demandam pouco esforço |
||||
para serem criadas. No entanto, se você deseja reutilizar algumas ações em diferentes |
||||
lugares ou se deseja distribuir uma ação, deve considerar defini-la como uma *ação standalone*. |
||||
|
||||
|
||||
### Ações Standalone <a name="standalone-actions"></a> |
||||
|
||||
Ações standalone são definidas por classes de ações que estendem de [[yii\base\Action]] |
||||
ou de suas classes filhas. |
||||
Por example, nas versões do Yii, existe a [[yii\web\ViewAction]] e a [[yii\web\ErrorAction]], ambas são ações standalone. |
||||
|
||||
Para usar uma ação standalone, você deve *mapear as ações* sobrescrevendo o método |
||||
[[yii\base\Controller::actions()]] em suas classes controllers como o seguinte: |
||||
|
||||
```php |
||||
public function actions() |
||||
{ |
||||
return [ |
||||
// declara a ação "error" usando um nome de classe |
||||
'error' => 'yii\web\ErrorAction', |
||||
|
||||
// declara a ação "view" usando uma configuração em array |
||||
'view' => [ |
||||
'class' => 'yii\web\ViewAction', |
||||
'viewPrefix' => '', |
||||
], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
Como pode ver, o método `actions()` deve retornar um array cujas chaves são os IDs |
||||
das ações e os valores correspondentes ao nome da classe da ação ou [configurações](concept-configurations.md). Ao contrário das ações inline, os IDs das ações standalone |
||||
podem conter caracteres arbitrários desde que sejam mapeados no método `actions()`. |
||||
|
||||
|
||||
Para criar uma classe de ação standalone, você deve estender de [[yii\base\Action]] ou de duas classes filhas e implementar um método público chamado `run()`. A regra para o método `run()` |
||||
é semelhante ao de um método de ação. Por exemplo, |
||||
|
||||
```php |
||||
<?php |
||||
namespace app\components; |
||||
|
||||
use yii\base\Action; |
||||
|
||||
class HelloWorldAction extends Action |
||||
{ |
||||
public function run() |
||||
{ |
||||
return "Hello World"; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
|
||||
### Resultados da Ação <a name="action-results"></a> |
||||
|
||||
O valor de retorno do método de ação ou do método `run()` de uma ação standalone |
||||
são importantes. Eles representam o resultado da ação correspondente. |
||||
|
||||
O valor de retorno pode ser um objeto de [resposta](runtime-responses.md) que |
||||
que enviará como resposta aos usuários finais. |
||||
|
||||
* Para [[yii\web\Application|aplicações Web]], o valor de retorno também poder |
||||
ser algum dado arbitrário que será atribuído à propriedade [[yii\web\Response::data]] |
||||
e ainda ser convertido em uma string para representar o corpo da resposta. |
||||
* Para [[yii\console\Application|aplicações console]], o valor de retorno também |
||||
poder ser um inteiro representando o [[yii\console\Response::exitStatus|exit status]] |
||||
(status de saída) da execução do comando. |
||||
|
||||
Nos exemplos acima, todos os resultados são strings que serão tratados como o |
||||
corpo das respostas para serem enviados aos usuários finais. No exemplo a seguir, |
||||
mostra como uma ação pode redirecionar o navegador do usuário para uma nova URL |
||||
retornando um objeto de resposta (o método [[yii\web\Controller::redirect()|redirect()]] |
||||
retorna um objeto de resposta): |
||||
|
||||
```php |
||||
public function actionForward() |
||||
{ |
||||
// redireciona o navegador do usuário para http://example.com |
||||
return $this->redirect('http://example.com'); |
||||
} |
||||
``` |
||||
|
||||
|
||||
### Parâmetros da Ação <a name="action-parameters"></a> |
||||
|
||||
Os métodos de ações para as ações inline e os métodos `run()` para as ações |
||||
standalone podem receber parâmetros, chamados *parâmetros da ação*. |
||||
Seus valores são obtidos a partir das requisições. Para |
||||
[[yii\web\Application|aplicações Web]], o valor de cada parâmetro da ação são |
||||
obtidos pelo `$_GET` usando o nome do parâmetro como chave; para |
||||
[[yii\console\Application|aplicações console]], eles correspondem aos argumentos |
||||
da linha de comando. |
||||
|
||||
No exemplo a seguir, a ação `view` (uma ação inline) possui dois parâmetros declarados: |
||||
`$id` e `$version`. |
||||
|
||||
```php |
||||
namespace app\controllers; |
||||
|
||||
use yii\web\Controller; |
||||
|
||||
class PostController extends Controller |
||||
{ |
||||
public function actionView($id, $version = null) |
||||
{ |
||||
// ... |
||||
} |
||||
} |
||||
``` |
||||
|
||||
A seguir, os parâmetros da ação serão populados em diferentes requisições: |
||||
|
||||
* `http://hostname/index.php?r=post/view&id=123`: o parâmetro `$id` receberá |
||||
o valor `'123'`, enquanto o `$version` continuará com o valor nulo porque não |
||||
existe o parâmetro `version` na URL. |
||||
* `http://hostname/index.php?r=post/view&id=123&version=2`: os parâmetros `$id` |
||||
e `$version` serão receberão os valores `'123'` e `'2'`, respectivamente. |
||||
* `http://hostname/index.php?r=post/view`: uma exceção [[yii\web\BadRequestHttpException]] |
||||
será lançada porque o parâmetro obrigatório `$id` não foi informado na requisição. |
||||
* `http://hostname/index.php?r=post/view&id[]=123`: uma exceção [[yii\web\BadRequestHttpException]] |
||||
será lançada porque o parâmetro `$id` foi informado com um valor array `['123']` |
||||
na qual não era esperado. |
||||
|
||||
Se você quiser que um parâmetro da ação aceite valores arrays, deverá declara-lo |
||||
explicitamente com `array`, como mostro a seguir: |
||||
|
||||
```php |
||||
public function actionView(array $id, $version = null) |
||||
{ |
||||
// ... |
||||
} |
||||
``` |
||||
|
||||
Agora, se a requisição for `http://hostname/index.php?r=post/view&id[]=123`, o |
||||
parâmetro `$id` receberá o valor `['123']`. Se a requisição for |
||||
`http://hostname/index.php?r=post/view&id=123`, o parâmetro `$id` ainda receberá |
||||
um array como valor pois o valor escalar `'123'` será convertido automaticamente |
||||
em um array. |
||||
|
||||
Os exemplo acima mostram, principalmente, como os parâmetros da ação trabalham em |
||||
aplicações Web. Para aplicações console, por favor, consulte a seção |
||||
[Comandos de Console](tutorial-console.md) para mais detalhes. |
||||
|
||||
|
||||
### Default Action <a name="default-action"></a> |
||||
|
||||
Cada controller tem uma ação padrão especificado pela propriedade |
||||
[[yii\base\Controller::defaultAction]]. |
||||
Quando uma [rota](#id-da-rota) contém apenas o ID do controller, implica que a |
||||
ação padrão do controller seja solicitada. |
||||
|
||||
Por padrão, a ação padrão é definida como `index`. Se quiser alterar o valor padrão, |
||||
simplesmente sobrescreva esta propriedade na classe controller, como o seguinte: |
||||
|
||||
```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 do Controller <a name="controller-lifecycle"></a> |
||||
|
||||
Ao processar uma requisição, a [aplicação](structure-applications.md) criará |
||||
um controller baseada na [rota](#routes) solicitada. O controller, então, se submeterá |
||||
ao seguinte ciclo de vida para concluir a requisição: |
||||
|
||||
1. O método [[yii\base\Controller::init()]] é chamado após o controller ser criado e configurado. |
||||
2. O controller cria um objeto da ação baseada no ID da ação solicitada: |
||||
* Se o ID da ação não for especificado, o [[yii\base\Controller::defaultAction|ID da ação padrão]] será utilizada. |
||||
* Se o ID da ação for encontrada no [[yii\base\Controller::actions()|mapeamento das ações]], uma ação standalone será criada; |
||||
* Se o ID da ação for encontrada para corresponder a um método de ação, uma ação inline será criada; |
||||
* Caso contrário, uma exceção [[yii\base\InvalidRouteException]] será lançada. |
||||
3. De forma sequencial, o controller chama o método `beforeAction()` da aplicação, o módulo (se o controller pertencer a um módulo) e o controller. |
||||
* Se uma das chamadas retornar false, o restante dos métodos `beforeAction()` serão ignoradas e a execução da ação será cancelada. |
||||
* Por padrão, cada método `beforeAction()` desencadeia a execução de um evento chamado `beforeAction` na qual você pode associar a uma função (handler). |
||||
4. O controller executa a ação: |
||||
* Os parâmetros da ação serão analizados e populados a partir dos dados obtidos pela requisição; |
||||
5. De forma sequencial, o controller chama o método `afterAction()` do controller, o módulo (se o controller pertencer a um módulo) e a aplicação. |
||||
* Por padrão, cada método `afterAction()` desencadeia a execução de um evento chamado `afterAction` na qual você pode associar a uma função (handler). |
||||
6. A aplicação obterá o resultado da ação e irá associá-lo na [resposta](runtime-responses.md). |
||||
|
||||
|
||||
## Best Practices <a name="best-practices"></a> |
||||
|
||||
Em uma aplicação bem projetada, frequentemente os controllers são bem pequenos na |
||||
qual cada ação possui poucas linhas de códigos. |
||||
Se o controller for um pouco complicado, geralmente indica que terá que refaze-lo |
||||
e passar algum código para outro classe. |
||||
|
||||
Em resumo, os controllers: |
||||
|
||||
* podem acessar os dados de uma [requisição](runtime-requests.md); |
||||
* podem chamar os métodos dos [models](structure-models.md) e outros componentes |
||||
de serviço com dados da requisição; |
||||
* podem usar as [views](structure-views.md) para compor as respostas; |
||||
* NÃO devem processar os dados da requisição - isto deve ser feito pelos [models](structure-models.md); |
||||
* devem evitar inserir códigos HTML ou outro código de apresentação - é melhor |
||||
que sejam feitos nas [views](structure-views.md). |
@ -0,0 +1,578 @@
|
||||
Models (Modelos) |
||||
================ |
||||
|
||||
Os models (modelos) fazem parte da arquitetura [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller). |
||||
Eles representam os dados, as regras e a lógica de negócio. |
||||
|
||||
Você pode criar uma classe model estendendo de [[yii\base\Model]] ou de seus filhos. |
||||
A classe base [[yii\base\Model]] suporta muitos recursos úteis: |
||||
|
||||
* [Atributos](#attributes): representa os dados de negócio e podem ser acessados |
||||
normalmente como uma propriedade de objeto ou como um elemento de array; |
||||
* [Labels dos atributos](#attribute-labels): especifica os labels de exibição dos |
||||
atributos; |
||||
* [Atribuição em massa](#massive-assignment): suporta popular vários atributos em |
||||
uma única etapa; |
||||
* [Regras de validação](#validation-rules): garante que os dados de entrada sejam |
||||
baseadas nas regras de validação que foram declaradas; |
||||
* [Data Exporting](#data-exporting): permite que os dados de model a serem exportados |
||||
em array possuam formatos personalizados. |
||||
|
||||
A classe `Model` também é a classe base para models mais avançados, como o [Active Record](db-active-record.md). |
||||
Por favor, consulte a documentação relevante para mais detalhes sobre estes models mais avançados. |
||||
|
||||
> Informação: Você não é obrigado basear suas classe model em [[yii\base\Model]]. |
||||
> No entanto, por existir muitos componentes do Yii construídos para suportar o |
||||
> [[yii\base\Model]], normalmente é a classe base preferível para um model. |
||||
|
||||
|
||||
## Atributos <a name="attributes"></a> |
||||
|
||||
Os models representam dados de negócio por meio de *atributos*. Cada atributo é |
||||
uma propriedade publicamente acessível de um model. O método [[yii\base\Model::attributes()]] |
||||
especifica quais atributos de uma classe model possuirá. |
||||
|
||||
Você pode acessar um atributo como fosse uma propriedade normal de um objeto: |
||||
|
||||
```php |
||||
$model = new \app\models\ContactForm; |
||||
|
||||
// "name" é um atributo de ContactForm |
||||
$model->name = 'example'; |
||||
echo $model->name; |
||||
``` |
||||
|
||||
Você também pode acessar os atributos como elementos de um array, graças ao suporte |
||||
de [ArrayAccess](http://php.net/manual/en/class.arrayaccess.php) e |
||||
[ArrayIterator](http://php.net/manual/en/class.arrayiterator.php) pelo |
||||
[[yii\base\Model]]: |
||||
|
||||
```php |
||||
$model = new \app\models\ContactForm; |
||||
|
||||
// acessando atributos como elementos de array |
||||
$model['name'] = 'example'; |
||||
echo $model['name']; |
||||
|
||||
// iterando sobre os atributos |
||||
foreach ($model as $name => $value) { |
||||
echo "$name: $value\n"; |
||||
} |
||||
``` |
||||
|
||||
|
||||
### Definindo Atributos <a name="defining-attributes"></a> |
||||
|
||||
Por padrão, se a classe model estender diretamente de [[yii\base\Model]], todas |
||||
as suas variáveis públicas e não estáticas serão atributos. Por exemplo, a classe |
||||
model `ContactForm` a seguir possui quatro atributos: `name`, `email`, `subject` |
||||
e `body`. O model `ContactForm` é usado para representar os dados de entrada obtidos |
||||
a partir de um formulário HTML. |
||||
|
||||
```php |
||||
namespace app\models; |
||||
|
||||
use yii\base\Model; |
||||
|
||||
class ContactForm extends Model |
||||
{ |
||||
public $name; |
||||
public $email; |
||||
public $subject; |
||||
public $body; |
||||
} |
||||
``` |
||||
|
||||
|
||||
Você pode sobrescrever o método [[yii\base\Model::attributes()]] para definir |
||||
atributos de uma forma diferente. Este método deve retornar os nomes dos atributos |
||||
em um model. Por exemplo, o [[yii\db\ActiveRecord]] faz com que o método retorne |
||||
os nomes das colunas da tabela do banco de dados como nomes de atributos. |
||||
Observe que também poderá sobrescrever os métodos mágicos tais como `__get()` e |
||||
`__set()`, para que os atributos poderem ser acessados como propriedades normais |
||||
de objetos. |
||||
|
||||
|
||||
### Labels dos Atributos <a name="attribute-labels"></a> |
||||
|
||||
Ao exibir valores ou obter dados de entrada dos atributos, muitas vezes é necessário |
||||
exibir alguns labels associados aos atributos. Por exemplo, dado um atributo chamado |
||||
`firstName`, você pode querer exibir um label `First Name` que é mais amigável |
||||
quando exibido aos usuários finais como em formulários e mensagens de erro. |
||||
|
||||
Você pode obter o label de um atributo chamando o método [[yii\base\Model::getAttributeLabel()]]. |
||||
Por exemplo, |
||||
|
||||
```php |
||||
$model = new \app\models\ContactForm; |
||||
|
||||
// displays "Name" |
||||
echo $model->getAttributeLabel('name'); |
||||
``` |
||||
|
||||
Por padrão, os labels dos atributos automaticamente serão gerados com os nomes dos |
||||
atributos. Isto é feito pelo método [[yii\base\Model::generateAttributeLabel()]]. |
||||
Ele transforma os nomes camel-case das variáveis em várias palavras, colocando em |
||||
caixa alta a primeira letra de cada palavra. Por exemplo, `username` torna-se |
||||
`Username`, enquanto `firstName` torna-se `First Name`. |
||||
|
||||
Se você não quiser usar esta geração automática do labels, poderá sobrescrever o |
||||
método [[yii\base\Model::attributeLabels()]] declarando explicitamente os labels |
||||
dos atributos. Por exemplo, |
||||
|
||||
```php |
||||
namespace app\models; |
||||
|
||||
use yii\base\Model; |
||||
|
||||
class ContactForm extends Model |
||||
{ |
||||
public $name; |
||||
public $email; |
||||
public $subject; |
||||
public $body; |
||||
|
||||
public function attributeLabels() |
||||
{ |
||||
return [ |
||||
'name' => 'Your name', |
||||
'email' => 'Your email address', |
||||
'subject' => 'Subject', |
||||
'body' => 'Content', |
||||
]; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Para aplicações que suportam vários idiomas, você pode querer traduzir os labels |
||||
dos atributos. Isto também é feito no método [[yii\base\Model::attributeLabels()|attributeLabels()]], |
||||
conforme o exemplo a seguir: |
||||
|
||||
```php |
||||
public function attributeLabels() |
||||
{ |
||||
return [ |
||||
'name' => \Yii::t('app', 'Your name'), |
||||
'email' => \Yii::t('app', 'Your email address'), |
||||
'subject' => \Yii::t('app', 'Subject'), |
||||
'body' => \Yii::t('app', 'Content'), |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
Você pode até definir condicionalmente os labels dos atributos. Por exemplo, baseado |
||||
no [cenário](#scenarios) que o model estiver utilizando, você pode retornar diferentes |
||||
labels para o mesmo atributo. |
||||
|
||||
> Informação: Estritamente falando, os labels dos atributos fazem parte das |
||||
[views](structure-views.md) (visões). Mas ao declarar os labels em models (modelos), |
||||
frequentemente tornam-se mais convenientes e podem resultar um código mais limpo |
||||
e reutilizável. |
||||
|
||||
|
||||
## Cenários <a name="scenarios"></a> |
||||
|
||||
Um model (modelo) pode ser usado em diferentes *cenários*. Por exemplo, um model |
||||
`User` pode ser usado para obter dados de entrada de login, mas também pode ser |
||||
usado com a finalidade de registrar o usuário. Em diferentes cenários, um model |
||||
pode usar diferentes regras e lógicas de negócio. Por exemplo, um atributo `email` |
||||
pode ser obrigatório durante o cadastro do usuário, mas não durante ao login. |
||||
|
||||
Um model (modelo) usa a propriedade [[yii\base\Model::scenario]] para identificar |
||||
o cenário que está sendo usado. |
||||
Por padrão, um model (modelo) suporta apenas um único cenário chamado `default`. |
||||
O código a seguir mostra duas formas de definir o cenário de um model (modelo): |
||||
|
||||
```php |
||||
// o cenário é definido pela propriedade |
||||
$model = new User; |
||||
$model->scenario = 'login'; |
||||
|
||||
// o cenário é definido por meio de configuração |
||||
$model = new User(['scenario' => 'login']); |
||||
``` |
||||
|
||||
Por padrão, os cenários suportados por um model (modelo) são determinados pelas |
||||
[regras de validação](#validation-rules) declaradas no próprio model (modelo). |
||||
No entanto, você pode personalizar este comportamento sobrescrevendo o método |
||||
[[yii\base\Model::scenarios()]], conforme o exemplo a seguir: |
||||
|
||||
```php |
||||
namespace app\models; |
||||
|
||||
use yii\db\ActiveRecord; |
||||
|
||||
class User extends ActiveRecord |
||||
{ |
||||
public function scenarios() |
||||
{ |
||||
return [ |
||||
'login' => ['username', 'password'], |
||||
'register' => ['username', 'email', 'password'], |
||||
]; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
> Informação: Nos exemplos anteriores, as classes model (model) são estendidas de |
||||
[[yii\db\ActiveRecord]] por usarem diversos cenários para auxiliarem as classes |
||||
[Active Record](db-active-record.md) classes. |
||||
|
||||
O método `scenarios()` retorna um array cujas chaves são os nomes dos cenários e |
||||
os valores que correspondem aos *active attributes* (atributo ativo). Um atributo |
||||
ativo podem ser [atribuídos em massa](#massive-assignment) e é sujeito a |
||||
[validação](#validation-rules). No exemplo anterior, os atributos `username` e |
||||
`password` são ativos no cenário `login`; enquanto no cenário `register`, além |
||||
dos atribitos `username` e `password`, o atributo `email` passará a ser ativo. |
||||
|
||||
A implementação padrão do método `scenarios()` retornará todos os cenários encontrados |
||||
nas regras de validação declaradas no método [[yii\base\Model::rules()]]. Ao |
||||
sobrescrever o método `scenarios()`, se quiser introduzir novos cenários, além |
||||
dos cenários padrão, poderá escrever um código conforme o exemplo a seguir: |
||||
|
||||
```php |
||||
namespace app\models; |
||||
|
||||
use yii\db\ActiveRecord; |
||||
|
||||
class User extends ActiveRecord |
||||
{ |
||||
public function scenarios() |
||||
{ |
||||
$scenarios = parent::scenarios(); |
||||
$scenarios['login'] = ['username', 'password']; |
||||
$scenarios['register'] = ['username', 'email', 'password']; |
||||
return $scenarios; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
O recurso de cenários são usados principalmente para [validação](#validation-rules) |
||||
e para [atribuição em massa](#massive-assignment). |
||||
Você pode, no entanto, usá-lo para outros fins. Por exemplo, você pode declarar |
||||
diferentes [labels para os atributos](#attribute-labels) baseados no cenário atual. |
||||
|
||||
|
||||
## Regras de Validação <a name="validation-rules"></a> |
||||
|
||||
Quando os dados para um model (modelo) são recebidos de usuários finais, devem ser |
||||
validados para garantir que satisfazem as regras (*regras de validação*, também |
||||
conhecidos como *regras de negócio*). Por exemplo, considerando um model (modelo) |
||||
`ContactForm`, você pode querer garantir que todos os atributos não sejam vazios e |
||||
que o atributo `email` contenha um e-mail válido. |
||||
Se o valor de algum atributo não satisfizer a regra de negócio correspondente, |
||||
mensagens apropriadas de erros serão exibidas para ajudar o usuário a corrigi-los. |
||||
|
||||
Você pode chamar o método [[yii\base\Model::validate()]] para validar os dados |
||||
recebidos. O método usará as regras de validação declaradas em [[yii\base\Model::rules()]] |
||||
para validar todos os atributos relevantes. Se nenhum erro for encontrado, o método |
||||
retornará true. Caso contrário, o método irá manter os erros na propriedade |
||||
[[yii\base\Model::errors]] e retornará false. Por exemplo, |
||||
|
||||
```php |
||||
$model = new \app\models\ContactForm; |
||||
|
||||
// os atributos do model serão populados pelos dados fornecidos pelo usuário |
||||
$model->attributes = \Yii::$app->request->post('ContactForm'); |
||||
|
||||
if ($model->validate()) { |
||||
// todos os dados estão válidos |
||||
} else { |
||||
// a validação falhou: $errors é um array contendo as mensagens de erro |
||||
$errors = $model->errors; |
||||
} |
||||
``` |
||||
|
||||
|
||||
Para declarar as regras de validação em um model (modelo), sobrescreva o método |
||||
[[yii\base\Model::rules()]] retornando as regras que os atributos do model (modelo) |
||||
devem satisfazer. O exemplo a seguir mostra as regras de validação sendo declaradas |
||||
no model (modelo) `ContactForm`: |
||||
|
||||
```php |
||||
public function rules() |
||||
{ |
||||
return [ |
||||
// os atributos name, email, subject e body são obrigatórios |
||||
[['name', 'email', 'subject', 'body'], 'required'], |
||||
|
||||
// o atributo email deve ter um e-mail válido |
||||
['email', 'email'], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
Uma regra pode ser usada para validar um ou vários atributos e, um atributo pode |
||||
ser validado por uma ou várias regras. |
||||
Por favor, consulte a seção [Validação de Dados](input-validation.md) para mais |
||||
detalhes sobre como declarar regras de validação. |
||||
|
||||
Às vezes, você pode querer que uma regra se aplique apenas em determinados |
||||
[cenários](#scenarios). Para fazer isso, você pode especificar a propriedade |
||||
`on` de uma regra, como o seguinte: |
||||
|
||||
```php |
||||
public function rules() |
||||
{ |
||||
return [ |
||||
// os atributos username, email e password são obrigatórios no cenario "register" |
||||
[['username', 'email', 'password'], 'required', 'on' => 'register'], |
||||
|
||||
// os atributos username e password são obrigatórios no cenario "login" |
||||
[['username', 'password'], 'required', 'on' => 'login'], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
Se você não especificar a propriedade `on`, a regra será aplicada em todos os |
||||
cenários. Uma regra é chamada de *active rule* (regra ativa), se ela puder ser |
||||
aplicada no [[yii\base\Model::scenario|cenário]] atual. |
||||
|
||||
Um atributo será validado, se e somente se, for um atributo ativo declarado no |
||||
método `scenarios()` e estiver associado a uma ou várias regras declaradas no método `rules()`. |
||||
|
||||
|
||||
## Atribuição em Massa <a name="massive-assignment"></a> |
||||
|
||||
Atribuição em massa é a forma conveniente para popular um model (modelo) com os |
||||
dados de entrada do usuário usando uma única linha de código. |
||||
Ele popula os atributos de um model (modelo) atribuindo os dados de entrada diretamente |
||||
na propriedade [[yii\base\Model::$attributes]]. Os dois códigos a seguir são |
||||
equivalentes, ambos tentam atribuir os dados do formulário enviados pelos usuários |
||||
finais para os atributos do model (modelo) `ContactForm`. Evidentemente, a |
||||
primeira forma, que utiliza a atribuição em massa, é a mais limpa e o menos |
||||
propenso a erros do que a segunda forma: |
||||
|
||||
```php |
||||
$model = new \app\models\ContactForm; |
||||
$model->attributes = \Yii::$app->request->post('ContactForm'); |
||||
``` |
||||
|
||||
```php |
||||
$model = new \app\models\ContactForm; |
||||
$data = \Yii::$app->request->post('ContactForm', []); |
||||
$model->name = isset($data['name']) ? $data['name'] : null; |
||||
$model->email = isset($data['email']) ? $data['email'] : null; |
||||
$model->subject = isset($data['subject']) ? $data['subject'] : null; |
||||
$model->body = isset($data['body']) ? $data['body'] : null; |
||||
``` |
||||
|
||||
|
||||
### Atributos Seguros <a name="safe-attributes"></a> |
||||
|
||||
A atribuição em massa só se aplica aos chamados *safe attributes* (atributos seguros), |
||||
que são os atributos listados no [[yii\base\Model::scenarios()]] para o |
||||
[[yii\base\Model::scenario|cenário]] atual de um model (modelo). |
||||
Por exemplo, se o model (modelo) `User` declarar o cenário como o código a seguir, |
||||
quando o cenário atual for `login`, apenas os atributos `username` e `password` |
||||
podem ser atribuídos em massa. Todos os outros atributos permanecerão inalterados. |
||||
|
||||
```php |
||||
public function scenarios() |
||||
{ |
||||
return [ |
||||
'login' => ['username', 'password'], |
||||
'register' => ['username', 'email', 'password'], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
> Informação: A razão da atribuição em massa só se aplicar para os atributos seguros |
||||
é para que você tenha o controle de quais atributos podem ser modificados pelos |
||||
dados dos usuário finais. Por exemplo, se o model (modelo) tiver um atributo |
||||
`permission` que determina a permissão atribuída ao usuário, você gostará que |
||||
apenas os administradores possam modificar este atributo através de uma interface backend. |
||||
|
||||
Como a implementação do método [[yii\base\Model::scenarios()]] retornará todos os |
||||
cenários e atributos encontrados em [[yii\base\Model::rules()]], se não quiser |
||||
sobrescrever este método, isto significa que um atributo é seguro desde que esteja |
||||
mencionado em uma regra de validação ativa. |
||||
|
||||
Por esta razão, uma alias especial de validação chamada `safe`, será fornecida |
||||
para que você possa declarar um atributo seguro, sem ser validado. Por exemplo, |
||||
a declaração da regra a seguir faz com que tanto o atributo `title` quanto o |
||||
`description` sejam seguros. |
||||
|
||||
```php |
||||
public function rules() |
||||
{ |
||||
return [ |
||||
[['title', 'description'], 'safe'], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
|
||||
### Atributos não Seguros <a name="unsafe-attributes"></a> |
||||
|
||||
Como descrito anteriormente, o método [[yii\base\Model::scenarios()]] serve para |
||||
dois propósitos: determinar quais atributos devem ser validados e quais atributos |
||||
são seguros. Em alguns casos raros, você pode quer validar um atributo sem marca-lo |
||||
como seguro. Para fazer isto, acrescente um ponto de exclamação `!` como prefixo |
||||
do nome do atributo ao declarar no método `scenarios()`, como o que foi feito no |
||||
atributo `secret` no exemplo a seguir: |
||||
|
||||
```php |
||||
public function scenarios() |
||||
{ |
||||
return [ |
||||
'login' => ['username', 'password', '!secret'], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
Quando o model (modelo) estiver no cenário `login`, todos os três atributos serão |
||||
validados. No entanto, apenas os atributos `username` e `password` poderão ser |
||||
atribuídos em massa. Para atribuir um valor de entrada no atributo `secret`, terá |
||||
que fazer isto explicitamente da seguinte forma: |
||||
|
||||
```php |
||||
$model->secret = $secret; |
||||
``` |
||||
|
||||
|
||||
## Exportação de Dados <a name="data-exporting"></a> |
||||
|
||||
Muitas vezes os models (modelos) precisam ser exportados em diferentes tipos de |
||||
formatos. Por exemplo, você pode querer converter um conjunto de models (modelos) |
||||
no formato JSON ou Excel. O processo de exportação pode ser divido em duas etapas independentes. |
||||
Na primeira etapa, os models (modelos) serão convertidos em arrays; na segunda |
||||
etapa, os arrays serão convertidos em um determinado formato. Se concentre apenas |
||||
na primeira etapa, uma vez que a segunda etapa pode ser alcançada por formatadores |
||||
de dados genéricos, tais como o [[yii\web\JsonResponseFormatter]]. |
||||
|
||||
A maneira mais simples de converter um model (modelo) em um array consiste no uso |
||||
da propriedade [[yii\base\Model::$attributes]]. |
||||
Por exemplo, |
||||
|
||||
```php |
||||
$post = \app\models\Post::findOne(100); |
||||
$array = $post->attributes; |
||||
``` |
||||
|
||||
Por padrão, a propriedade [[yii\base\Model::$attributes]] retornará os valores de |
||||
todos os atributos declarados no método [[yii\base\Model::attributes()]]. |
||||
|
||||
Uma maneira mais flexível e poderosa de converter um model (modelo) em um array é |
||||
através do método [[yii\base\Model::toArray()]]. O seu comportamento padrão é o |
||||
mesmo do [[yii\base\Model::$attributes]]. No entanto, ele permite que você escolha |
||||
quais itens de dados, chamados de *fields* (campos), devem ser mostrados no array |
||||
resultante e como eles devem vir formatados. |
||||
Na verdade, é a maneira padrão de exportação de models (modelos) no desenvolvimento |
||||
de Web services RESTful, como descrito na seção [Formatando Respostas](rest-response-formatting.md). |
||||
|
||||
|
||||
### Campos <a name="fields"></a> |
||||
|
||||
Um campo é simplesmente um elemento nomeado no array obtido pela chamada do método |
||||
[[yii\base\Model::toArray()]] de um model (modelo). |
||||
|
||||
Por padrão, os nomes dos campos são iguais aos nomes dos atributos. No entanto, |
||||
você pode alterar este comportamento sobrescrevendo os métodos |
||||
[[yii\base\Model::fields()|fields()]] e/ou [[yii\base\Model::extraFields()|extraFields()]]. |
||||
Ambos os métodos devem retornar uma lista dos campos definidos. Os campos definidos |
||||
pelo método `fields()` são os campos padrão, o que significa que o `toArray()` |
||||
retornará estes campos por padrão. O método `extraFields()` define, de forma adicional, |
||||
os campos disponíveis que também podem ser retornados pelo `toArray()`, contanto |
||||
que sejam especificados através do parâmetro `$expand`. Por exemplo, o código a |
||||
seguir retornará todos os campos definidos em `fields()` incluindo os campos |
||||
`prettyName` e `fullAddress`, a menos que estejam definidos no `extraFields()`. |
||||
|
||||
```php |
||||
$array = $model->toArray([], ['prettyName', 'fullAddress']); |
||||
``` |
||||
|
||||
Você poderá sobrescrever o método `fields()` para adicionar, remover, renomear ou |
||||
redefinir os campos. O valor de retorno do `fields()` deve ser um array. As chaves |
||||
do array não os nomes dos campos e os valores correspondem ao nome do atributo |
||||
definido, na qual, podem ser tanto os nomes de propriedades/atributos quanto funções |
||||
anônimas que retornam o valor dos campos correspondentes. Em um caso especial, |
||||
quando o nome do campo for igual ao nome do atributo definido, você poderá omitir |
||||
a chave do array. Por exemplo, |
||||
|
||||
```php |
||||
// usar uma lista explicita de todos os campos lhe garante que qualquer mudança |
||||
// em sua tabela do banco de dados ou atributos do model (modelo) não altere os |
||||
// nomes de seus campos (para manter compatibilidade com versões anterior da API). |
||||
public function fields() |
||||
{ |
||||
return [ |
||||
// o nome do campos é igual ao nome do atributo |
||||
'id', |
||||
|
||||
// o nome do campo é "email", o nome do atributo correspondente é "email_address" |
||||
'email' => 'email_address', |
||||
|
||||
// o nome do campo é "name", o seu valor é definido por uma função call-back do PHP |
||||
'name' => function () { |
||||
return $this->first_name . ' ' . $this->last_name; |
||||
}, |
||||
]; |
||||
} |
||||
|
||||
// filtra alguns campos, é bem usado quando você quiser herdar a implementação |
||||
// da classe pai e remover alguns campos delicados. |
||||
public function fields() |
||||
{ |
||||
$fields = parent::fields(); |
||||
|
||||
// remove os campos que contém informações delicadas |
||||
unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']); |
||||
|
||||
return $fields; |
||||
} |
||||
``` |
||||
|
||||
> Atenção: Como, por padrão, todos os atributos de um model (modelo) serão |
||||
>incluídos no array exportado, você deve examinar seus dados para ter certeza |
||||
>que não possuem informações delicadas. Se existir, deverá sobrescrever o método |
||||
>`fields()` para remove-los. No exemplo anterior, nós decidimos remover os |
||||
>campos `auth_key`, `password_hash` e `password_reset_token`. |
||||
|
||||
|
||||
## Boas Práticas <a name="best-practices"></a> |
||||
|
||||
A representação dos dados, regras e lógicas de negócios estão centralizados nos |
||||
models (modelos). Muitas vezes precisam ser reutilizadas em lugares diferentes. |
||||
Em um aplicativo bem projetado, models (modelos) geralmente são muitos maiores |
||||
que os [controllers](structure-controllers.md) |
||||
|
||||
Em resumo, os models (modelos): |
||||
|
||||
* podem conter atributos para representar os dados de negócio; |
||||
* podem conter regras de validação para garantir a validade e integridade dos dados; |
||||
* podem conter métodos para implementar lógicas de negócio; |
||||
* NÃO devem acessar diretamente as requisições, sessões ou quaisquer dados do |
||||
ambiente do usuário. Os models (modelos) devem receber estes dados a partir dos |
||||
[controllers (controladores)](structure-controllers.md); |
||||
* devem evitar inserir HTML ou outros códigos de apresentação – isto deve ser |
||||
feito nas [views (visões)](structure-views.md); |
||||
* devem evitar ter muitos [cenários](#scenarios) em um único model (modelo). |
||||
|
||||
Você deve considerar em utilizar com mais frequência a última recomendação acima |
||||
quando desenvolver sistemas grandes e complexos. |
||||
Nestes sistemas, os models (modelos) podem ser bem grandes, pois são usados em |
||||
muitos lugares e podendo, assim, conter muitas regras e lógicas de negócio. |
||||
Nestes casos, a manutenção do código de um model (modelo) pode se transformar |
||||
em um pesadelo, na qual uma simples mudança no código pode afetar vários lugares |
||||
diferentes. Para desenvolver um model (modelo) manutenível, você pode seguir a |
||||
seguinte estratégia: |
||||
|
||||
* Definir um conjunto de classes model (modelo) base que são compartilhados por |
||||
diferentes [aplicações](structure-applications.md) ou [módulos](structure-modules.md). |
||||
Estas classes model (modelo) base deve contem um conjunto mínimo de regras e lógicas |
||||
de negocio que são comuns entre os locais que as utilizem. |
||||
* Em cada [aplicação](structure-applications.md) ou [módulo](structure-modules.md) |
||||
que usa um model (modelo), deve definir uma classe model (modelo) concreta que |
||||
estenderá a classe model (modelo) base que a corresponde. A classe model (modelo) |
||||
concreta irá conter apenas as regras e lógicas que são específicas de uma aplicação |
||||
ou módulo. |
||||
|
||||
Por exemplo, no [Modelo de Aplicação Avançada](tutorial-advanced-app.md), você |
||||
pode definir uma classe model (modelo) base `common\models\Post`. Em seguida, |
||||
para a aplicação front-end, você define uma classe model (modelo) concreta |
||||
`frontend\models\Post` que estende de `common\models\Post`. E de forma similar |
||||
para a aplicação back-end, você define a `backend\models\Post`. Com essa estratégia, |
||||
você garantirá que o `frontend\models\Post` terá apenas códigos específicos da |
||||
aplicação front-end e, se você fizer qualquer mudança nele, não precisará se |
||||
preocupar se esta mudança causará erros na aplicação back-end. |
@ -0,0 +1,512 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
||||
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd"> |
||||
<!--Created by yEd 3.13--> |
||||
<key for="graphml" id="d0" yfiles.type="resources"/> |
||||
<key for="port" id="d1" yfiles.type="portgraphics"/> |
||||
<key for="port" id="d2" yfiles.type="portgeometry"/> |
||||
<key for="port" id="d3" yfiles.type="portuserdata"/> |
||||
<key attr.name="url" attr.type="string" for="node" id="d4"/> |
||||
<key attr.name="description" attr.type="string" for="node" id="d5"/> |
||||
<key for="node" id="d6" yfiles.type="nodegraphics"/> |
||||
<key attr.name="Description" attr.type="string" for="graph" id="d7"/> |
||||
<key attr.name="url" attr.type="string" for="edge" id="d8"/> |
||||
<key attr.name="description" attr.type="string" for="edge" id="d9"/> |
||||
<key for="edge" id="d10" yfiles.type="edgegraphics"/> |
||||
<graph edgedefault="directed" id="G"> |
||||
<data key="d7"/> |
||||
<node id="n0" yfiles.foldertype="group"> |
||||
<data key="d4"/> |
||||
<data key="d6"> |
||||
<y:ProxyAutoBoundsNode> |
||||
<y:Realizers active="0"> |
||||
<y:GroupNode> |
||||
<y:Geometry height="570.7368214925131" width="763.2772213171534" x="-1269.9373595143054" y="-206.46394602457679"/> |
||||
<y:Fill color="#FFCC0024" transparent="false"/> |
||||
<y:BorderStyle hasColor="false" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="node_width" backgroundColor="#FFCC00" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="763.2772213171534" x="0.0" y="0.0">Вхідний скрипт (index.php або yii)</y:NodeLabel> |
||||
<y:Shape type="rectangle"/> |
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/> |
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/> |
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/> |
||||
</y:GroupNode> |
||||
<y:GroupNode> |
||||
<y:Geometry height="50.0" width="50.0" x="313.2978515625" y="225.33495140075684"/> |
||||
<y:Fill color="#F5F5F5" transparent="false"/> |
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/> |
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 4</y:NodeLabel> |
||||
<y:Shape type="roundrectangle"/> |
||||
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/> |
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/> |
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/> |
||||
</y:GroupNode> |
||||
</y:Realizers> |
||||
</y:ProxyAutoBoundsNode> |
||||
</data> |
||||
<graph edgedefault="directed" id="n0:"> |
||||
<node id="n0::n0"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="30.0" width="324.9258883570935" x="-1249.511914911339" y="-169.79793039957679"/> |
||||
<y:Fill color="#FFFFFF" transparent="false"/> |
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="219.583984375" x="52.67095199104665" y="5.93359375">Завантаження конфігурації додатка<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:Shape type="rectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n0::n1" yfiles.foldertype="group"> |
||||
<data key="d4"/> |
||||
<data key="d6"> |
||||
<y:ProxyAutoBoundsNode> |
||||
<y:Realizers active="0"> |
||||
<y:GroupNode> |
||||
<y:Geometry height="308.66601562499994" width="330.35133296005984" x="-1254.9373595143054" y="35.983324686686274"/> |
||||
<y:Fill color="#FFEFD6" transparent="false"/> |
||||
<y:BorderStyle hasColor="false" type="dashed" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="node_width" backgroundColor="#FF9900" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="330.35133296005984" x="0.0" y="0.0">Створення екземпляру додатка</y:NodeLabel> |
||||
<y:Shape type="roundrectangle"/> |
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/> |
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/> |
||||
<y:BorderInsets bottom="19" bottomF="19.15489692687993" left="5" leftF="5.425444602966309" right="0" rightF="0.0" top="0" topF="0.0"/> |
||||
</y:GroupNode> |
||||
<y:GroupNode> |
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/> |
||||
<y:Fill color="#F5F5F5" transparent="false"/> |
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/> |
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 5</y:NodeLabel> |
||||
<y:Shape type="roundrectangle"/> |
||||
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/> |
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/> |
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/> |
||||
</y:GroupNode> |
||||
</y:Realizers> |
||||
</y:ProxyAutoBoundsNode> |
||||
</data> |
||||
<graph edgedefault="directed" id="n0::n1:"> |
||||
<node id="n0::n1::n0"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="30.0" width="294.92588835709347" x="-1234.511914911339" y="72.64934031168627"/> |
||||
<y:Fill color="#FFFFFF" transparent="false"/> |
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="49.814453125" x="122.55571761604665" y="5.93359375">preInit()<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:Shape type="rectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n0::n1::n1"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="30.0" width="294.92588835709347" x="-1234.511914911339" y="122.64524027506516"/> |
||||
<y:Fill color="#FFFFFF" transparent="false"/> |
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="192.185546875" x="51.37017074104665" y="5.93359375">Реєстрація обробника помилок<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:Shape type="rectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n0::n1::n2"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="30.0" width="294.92588835709347" x="-1234.511914911339" y="174.96110788981125"/> |
||||
<y:Fill color="#FFFFFF" transparent="false"/> |
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="226.3515625" x="34.28716292854665" y="5.93359375">Налаштування властивостей додатка<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:Shape type="rectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n0::n1::n3"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="30.0" width="294.92588835709347" x="-1234.511914911339" y="226.56779181162517"/> |
||||
<y:Fill color="#FFFFFF" transparent="false"/> |
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="30.677734375" x="132.12407699104665" y="5.93359375">init()<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:Shape type="rectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n0::n1::n4"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="30.0" width="294.9258883570935" x="-1234.511914911339" y="280.4944433848063"/> |
||||
<y:Fill color="#FFFFFF" transparent="false"/> |
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="68.283203125" x="113.32134261604665" y="5.93359375">bootstrap()<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:Shape type="rectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
</graph> |
||||
</node> |
||||
<node id="n0::n2" yfiles.foldertype="group"> |
||||
<data key="d4"/> |
||||
<data key="d6"> |
||||
<y:ProxyAutoBoundsNode> |
||||
<y:Realizers active="0"> |
||||
<y:GroupNode> |
||||
<y:Geometry height="410.9838918050131" width="324.9258883570935" x="-846.5860265542456" y="-169.08748118082679"/> |
||||
<y:Fill color="#FFEFD6" transparent="false"/> |
||||
<y:BorderStyle hasColor="false" type="dashed" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="node_width" backgroundColor="#FF9900" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="324.9258883570935" x="0.0" y="0.0">Виконання додатка</y:NodeLabel> |
||||
<y:Shape type="roundrectangle"/> |
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/> |
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/> |
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="1.1368683772161603E-13"/> |
||||
</y:GroupNode> |
||||
<y:GroupNode> |
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/> |
||||
<y:Fill color="#F5F5F5" transparent="false"/> |
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/> |
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 3</y:NodeLabel> |
||||
<y:Shape type="roundrectangle"/> |
||||
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/> |
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/> |
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/> |
||||
</y:GroupNode> |
||||
</y:Realizers> |
||||
</y:ProxyAutoBoundsNode> |
||||
</data> |
||||
<graph edgedefault="directed" id="n0::n2:"> |
||||
<node id="n0::n2::n0"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="30.0" width="294.9258883570935" x="-831.5860265542456" y="-132.42146555582667"/> |
||||
<y:Fill color="#99CC00" transparent="false"/> |
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="148.84375" x="73.04106917854676" y="5.93359375">EVENT_BEFORE_REQUEST<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:Shape type="rectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n0::n2::n1" yfiles.foldertype="group"> |
||||
<data key="d4"/> |
||||
<data key="d6"> |
||||
<y:ProxyAutoBoundsNode> |
||||
<y:Realizers active="0"> |
||||
<y:GroupNode> |
||||
<y:Geometry height="203.666015625" width="294.9258883570935" x="-831.5860265542456" y="-78.08748118082679"/> |
||||
<y:Fill color="#99336635" transparent="false"/> |
||||
<y:BorderStyle hasColor="false" type="dashed" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="node_width" backgroundColor="#993366" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#FFFFFF" visible="true" width="294.9258883570935" x="0.0" y="0.0">Обробка запиту</y:NodeLabel> |
||||
<y:Shape type="roundrectangle"/> |
||||
<y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/> |
||||
<y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/> |
||||
<y:BorderInsets bottom="8" bottomF="7.929194132486941" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/> |
||||
</y:GroupNode> |
||||
<y:GroupNode> |
||||
<y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/> |
||||
<y:Fill color="#F5F5F5" transparent="false"/> |
||||
<y:BorderStyle color="#000000" type="dashed" width="1.0"/> |
||||
<y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.666015625" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="63.75830078125" x="-6.879150390625" y="0.0">Folder 4</y:NodeLabel> |
||||
<y:Shape type="roundrectangle"/> |
||||
<y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/> |
||||
<y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/> |
||||
<y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/> |
||||
</y:GroupNode> |
||||
</y:Realizers> |
||||
</y:ProxyAutoBoundsNode> |
||||
</data> |
||||
<graph edgedefault="directed" id="n0::n2::n1:"> |
||||
<node id="n0::n2::n1::n0"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="30.0" width="264.9258883570935" x="-816.5860265542456" y="-41.421465555826785"/> |
||||
<y:Fill color="#FFFFFF" transparent="false"/> |
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="199.9375" x="32.49419417854676" y="5.93359375">Запит -> маршрут та параметри<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:Shape type="rectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n0::n2::n1::n1"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="30.0" width="264.9258883570935" x="-816.5860265542456" y="18.578534444173215"/> |
||||
<y:Fill color="#FFFFFF" transparent="false"/> |
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="228.267578125" x="18.329155116046763" y="5.93359375">Створення модуля, контролера та дії<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:Shape type="rectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n0::n2::n1::n2"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="30.0" width="264.9258883570935" x="-816.5860265542456" y="72.64934031168627"/> |
||||
<y:Fill color="#FFFFFF" transparent="false"/> |
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="86.78125" x="89.07231917854676" y="5.93359375">Виконання дії<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:Shape type="rectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
</graph> |
||||
</node> |
||||
<node id="n0::n2::n2"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="30.0" width="294.9258883570935" x="-831.5860265542456" y="149.20206960042316"/> |
||||
<y:Fill color="#99CC00" transparent="false"/> |
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="141.982421875" x="76.47173324104676" y="5.93359375">EVENT_AFTER_REQUEST<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:Shape type="rectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n0::n2::n3"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="30.0" width="294.92588835709347" x="-831.5860265542456" y="196.89641062418633"/> |
||||
<y:Fill color="#FFFFFF" transparent="false"/> |
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="263.611328125" x="15.657280116046763" y="5.93359375">Відправка відповіді кінцевому користувачу<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:Shape type="rectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
</graph> |
||||
</node> |
||||
<node id="n0::n3"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="30.0" width="324.9258883570935" x="-846.5860265542456" y="319.2728754679363"/> |
||||
<y:Fill color="#FFFFFF" transparent="false"/> |
||||
<y:BorderStyle color="#000000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="173.9921875" x="75.46685042854676" y="5.93359375">Завершення обробки запиту<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:Shape type="rectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
</graph> |
||||
</node> |
||||
<edge id="e0" source="n0" target="n0::n1"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="-179.83580569315376" sy="-180.3944529152355" tx="-13.869777491410105" ty="-154.8008369539754"/> |
||||
<y:LineStyle color="#666666" type="line" width="2.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="n0::e0" source="n0::n0" target="n0::n1"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#666666" type="line" width="2.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:EdgeLabel alignment="center" backgroundColor="#99CCFF" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="120.455078125" x="-137.9645704847153" y="78.82421875000006">Масив конфігурації<y:LabelModel> |
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="77.04379339868619" distanceToCenter="true" position="right" ratio="0.5" segment="0"/> |
||||
</y:ModelParameter> |
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/> |
||||
</y:EdgeLabel> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="n0::n1::e0" source="n0::n1::n0" target="n0::n1::n1"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#666666" type="line" width="2.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="n0::n1::e1" source="n0::n1::n1" target="n0::n1::n2"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#666666" type="line" width="2.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="n0::n1::e2" source="n0::n1::n2" target="n0::n1::n3"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#666666" type="line" width="2.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="n0::n1::e3" source="n0::n1::n3" target="n0::n1::n4"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#666666" type="line" width="2.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="n0::e1" source="n0::n1" target="n0::n2"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="2.8060680342755404" sy="-48.37646484374994" tx="-162.49660512430125" ty="105.53540293375653"/> |
||||
<y:LineStyle color="#666666" type="line" width="2.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="n0::n2::e0" source="n0::n2::n0" target="n0::n2::n1"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#666666" type="line" width="2.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="n0::n2::n1::e0" source="n0::n2::n1::n0" target="n0::n2::n1::n1"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#666666" type="line" width="2.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="n0::n2::n1::e1" source="n0::n2::n1::n1" target="n0::n2::n1::n2"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#666666" type="line" width="2.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="n0::n2::e1" source="n0::n2::n1" target="n0::n2::n2"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#666666" type="line" width="2.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="n0::n2::e2" source="n0::n2::n2" target="n0::n2::n3"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#666666" type="line" width="2.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="n0::e2" source="n0::n2" target="n0::n3"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#666666" type="line" width="2.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:EdgeLabel alignment="center" backgroundColor="#99CCFF" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="64.90234375" x="-96.45114634054266" y="28.626707953545406">Exit status<y:LabelModel> |
||||
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="63.99999999999999" distanceToCenter="true" position="right" ratio="0.47945569632951074" segment="-1"/> |
||||
</y:ModelParameter> |
||||
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/> |
||||
</y:EdgeLabel> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
</graph> |
||||
<data key="d0"> |
||||
<y:Resources/> |
||||
</data> |
||||
</graphml> |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 77 KiB |
@ -1,25 +1,27 @@
|
||||
Огляд |
||||
===== |
||||
|
||||
Yii додаток організований згідно шаблону проектування [модель-представлення-подія (MVC)](http://ru.wikipedia.org/wiki/Model-View-Controller). |
||||
[Моделі](structure-models.md) являють собою дані, бізнес логіку і бізнес правила; [представлення](structure-views.md) |
||||
відповідають за відображення інформації, в тому числі і на основі даних, отриманих з моделей; [контролери](structure-controllers.md) |
||||
приймають вхідні дані від користувача і перетворюють їх в зрозумілий для [моделей](structure-models.md) формат і команди, а також відповідають за відображення потрібного представлення. |
||||
Додатки Yii організовані згідно шаблону проектування |
||||
[модель-представлення-подія (MVC)](http://uk.wikipedia.org/wiki/Модель-вид-контролер). |
||||
[Моделі](structure-models.md) являють собою дані, бізнес логіку та бізнес правила; |
||||
[представлення](structure-views.md) відповідають за відображення даних моделей; |
||||
[контролери](structure-controllers.md) приймають вхідні дані від користувача і перетворюють їх у команди для |
||||
[моделей](structure-models.md) та [представлень](structure-views.md). |
||||
|
||||
Окрім MVC, Yii додаток також має наступні сутності: |
||||
|
||||
* [вхідні скрипти](structure-entry-scripts.md): це PHP скрипти, які доступні напряму кінцевому користувачу додатка. |
||||
Вони відповідають за запуск та опрацювання вхідного запиту; |
||||
Вони відповідають за запуск циклу обробки запиту. |
||||
* [додатки](structure-applications.md): це глобально доступні об’єкти, які відповідають за коректну роботу різних |
||||
компонентів додатка і їх координацію для обробки запиту; |
||||
компонентів додатка і їх координацію для обробки запиту. |
||||
* [компоненти додатку](structure-application-components.md): це об’єкти, зареєстровані в додатку і які надають |
||||
різноманітні можливості для обробки поточного запиту; |
||||
різноманітні можливості для обробки запитів. |
||||
* [модулі](structure-modules.md): це самодостатні пакети, що включають в себе повністю всі ресурси для MVC. |
||||
Додаток може бути організовано з допомогою декількох модулів; |
||||
* [фільтри](structure-filters.md): це код, який повинен бути виконаний до і після оброки запиту контролерами; |
||||
* [віджети](structure-widgets.md): це об’єкти, які можуть включати в себе [представлення](structure-views.md). |
||||
Вони можуть містити різноманітну логіку і використовуватись в різноманітних представленнях. |
||||
Додаток може бути організовано за допомогою декількох модулів. |
||||
* [фільтри](structure-filters.md): це код, який повинен бути виконаний до і після обробки запиту контролерами. |
||||
* [віджети](structure-widgets.md): це об’єкти, які можуть бути вбудованими у [представлення](structure-views.md). |
||||
Вони можуть містити різноманітну логіку і можуть бути повто використаними у різних представленнях. |
||||
|
||||
Нижче на діаграмі наведена структурна схема додатку: |
||||
На наступній діаграмі наведена структурна схема додатку: |
||||
|
||||
![Стандартна структура додатку](../guide/images/application-structure.png) |
||||
![Статична структура додатку](images/application-structure.png) |
||||
|
@ -1,6 +1,164 @@
|
||||
Security best practice |
||||
====================== |
||||
Security best practices |
||||
======================= |
||||
|
||||
> Note: This section is under development. |
||||
> |
||||
> It has no content yet. |
||||
Below we'll review common security principles and describe how to avoid threats when developing applications using Yii. |
||||
|
||||
Basic principles |
||||
---------------- |
||||
|
||||
There are two main principles when it comes to security no matter which application is being developed: |
||||
|
||||
1. Filter input. |
||||
2. Escape output. |
||||
|
||||
|
||||
### Filter input |
||||
|
||||
Filter input means that input should never be considered safe and you should always check if the value you've got is |
||||
actually among allowed ones i.e. if we know that sorting could be done by three fields `title`, `created_at` and `status` |
||||
and the field could be supplied via used input it's better to check the value we've got right where we're receiving it. |
||||
In terms of basic PHP that would look like the following: |
||||
|
||||
```php |
||||
$sortBy = $_GET['sort']; |
||||
if (!in_array($sortBy, ['title', 'created_at', 'status'])) { |
||||
throw new Exception('Invalid sort value.'); |
||||
} |
||||
``` |
||||
|
||||
In Yii, most probably you'll use [form validation](input-validation.md) to do alike checks. |
||||
|
||||
|
||||
### Escape output |
||||
|
||||
Escape output means that depending on context where we're using data it should be escaped i.e. in context of HTML you |
||||
should escape `<`, `>` and alike special characters. In context of JavaScript or SQL it will be different set of characters. |
||||
Since it's error-prone to escape everything manually Yii provides various tools to perform escaping for different |
||||
contexts. |
||||
|
||||
Avoiding SQL injections |
||||
----------------------- |
||||
|
||||
SQL injection happens when query text is formed by concatenating unescaped strings such as the following: |
||||
|
||||
```php |
||||
$username = $_GET['username']; |
||||
$sql = "SELECT * FROM user WHERE username = '$username'"; |
||||
``` |
||||
|
||||
Instead of supplying correct username attacker could give your applications something like `'; DROP TABLE user; --`. |
||||
Resulting SQL will be the following: |
||||
|
||||
```sql |
||||
SELECT * FROM user WHERE username = ''; DROP TABLE user; --' |
||||
``` |
||||
|
||||
This is valid query that will search for users with empty username and then will drop `user` table most probably |
||||
resulting in broken website and data loss (you've set up regular backups, right?). |
||||
|
||||
In Yii most of database querying happens via [Active Record](db-active-record.md) which properly uses PDO prepared |
||||
statements internally. In case of prepared statements it's not possible to manipulate query as was demonstrated above. |
||||
|
||||
Still, sometimes you need [raw queries](db-dao.md) or [query builder](db-query-builder.md). In this case you should use |
||||
safe ways of passing data. If data is used for column values it's preferred to use prepared statements: |
||||
|
||||
```php |
||||
// query builder |
||||
$userIDs = (new Query()) |
||||
->select('id') |
||||
->from('user') |
||||
->where('status=:status', [':status' => $status]) |
||||
->all(); |
||||
|
||||
// DAO |
||||
$userIDs = $connection |
||||
->createCommand('SELECT id FROM user where status=:status') |
||||
->bindValues([':status' => $status]) |
||||
->queryColumn(); |
||||
``` |
||||
|
||||
If data is used to specify column names or table names the best thing to do is to allow only predefined set of values: |
||||
|
||||
```php |
||||
function actionList($orderBy = null) |
||||
{ |
||||
if (!in_array($orderBy, ['name', 'status'])) { |
||||
throw new BadRequestHttpException('Only name and status are allowed to order by.') |
||||
} |
||||
|
||||
// ... |
||||
} |
||||
``` |
||||
|
||||
In case it's not possible, table and column names should be escaped. Yii has special syntax for such escaping |
||||
which allows doing it the same way for all databases it supports: |
||||
|
||||
```php |
||||
$sql = "SELECT COUNT([[$column]]) FROM {{table}}"; |
||||
$rowCount = $connection->createCommand($sql)->queryScalar(); |
||||
``` |
||||
|
||||
You can get details about the syntax in [Quoting Table and Column Names](db-dao.md#quoting-table-and-column-names). |
||||
|
||||
|
||||
Avoiding XSS |
||||
------------ |
||||
|
||||
XSS or cross-site scripting happens when output isn't escaped properly when outputting HTML to the browser. For example, |
||||
if user can enter his name and instead of `Alexander` he enters `<script>alert('Hello!');</script>`, every page that |
||||
outputs user name without escaping it will execute JavaScript `alert('Hello!');` resulting in alert box popping up |
||||
in a browser. Depending on website instead of innocent alert such script could send messages using your name or even |
||||
perform bank transactions. |
||||
|
||||
Avoiding XSS is quite easy in Yii. There are generally two cases: |
||||
|
||||
1. You want data to be outputted as plain text. |
||||
2. You want data to be outputted as HTML. |
||||
|
||||
If all you need is plain text then escaping is as easy as the following: |
||||
|
||||
|
||||
```php |
||||
<?= \yii\helpers\Html::encode($username) ?> |
||||
``` |
||||
|
||||
If it should be HTML we could get some help from HtmlPurifier: |
||||
|
||||
```php |
||||
<?= \yii\helpers\HtmlPurifier::process($description) ?> |
||||
``` |
||||
|
||||
Note that HtmlPurifier processing is quite heavy so consider adding caching. |
||||
|
||||
Avoiding CSRF |
||||
------------- |
||||
|
||||
TBD: what's CSRF, how it works, intro |
||||
|
||||
1. Follow HTTP specification i.e. GET should not change application state. |
||||
2. Keep Yii CSRF protection enabled. |
||||
|
||||
TBD: how CSRF protection works |
||||
|
||||
|
||||
Avoiding file exposure |
||||
---------------------- |
||||
|
||||
By default server webroot is meant to be pointed to `web` directory where `index.php` is. In case of shared hosting |
||||
environments it could be impossible to achieve so we'll end up with all the code, configs and logs in server webroot. |
||||
|
||||
If it's the case don't forget to deny access to everything except `web`. If it can't be done consider hosting your |
||||
application elsewhere. |
||||
|
||||
Avoiding debug info and tools at production |
||||
------------------------------------------- |
||||
|
||||
In debug mode Yii shows quite verbose errors which are certainly helpful for development. The thing is that these |
||||
verbose errors are handy for attacker as well since these could reveal database structure, configuration values and |
||||
parts of your code. Never run production applications with `YII_DEBUG` set to `true` in your `index.php`. |
||||
|
||||
You should never enalble Gii at production. It could be used to get information about database structure, code and to |
||||
simply rewrite code with what's generated by Gii. |
||||
|
||||
Debug toolbar should be avoided at production unless really necessary. It exposes all the application and config |
||||
details possible. If you absolutely need it check twice that access is proerly restricted to your IP only. |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue