# Conflicts: # framework/UPGRADE.md # framework/behaviors/SluggableBehavior.phptags/3.0.0-alpha1
@ -0,0 +1,939 @@
|
||||
Migración de Base de Datos |
||||
========================== |
||||
|
||||
Durante el curso de desarrollo y mantenimiento de una aplicación con base de datos, la estructura de dicha base de datos |
||||
evoluciona tanto como el código fuente. Por ejemplo, durante el desarrollo de una aplicación, |
||||
una nueva tabla podría ser necesaria; una vez que la aplicación se encuentra en producción, podría descrubrirse |
||||
que debería crearse un índice para mejorar el tiempo de ejecución de una consulta; y así sucesivamente. Debido a los cambios en la estructura de la base de datos |
||||
a menudo se requieren cambios en el código, Yii soporta la característica llamada *migración de base de datos*, la cual permite |
||||
tener un seguimiento de esos cambios en término de *migración de base de datos*, cuyo versionado es controlado |
||||
junto al del código fuente. |
||||
|
||||
Los siguientes pasos muestran cómo una migración puede ser utilizada por un equipo durante el desarrollo: |
||||
|
||||
1. Tim crea una nueva migración (por ej. crea una nueva table, cambia la definición de una columna, etc.). |
||||
2. Tim hace un commit con la nueva migración al sistema de control de versiones (por ej. Git, Mercurial). |
||||
3. Doug actualiza su repositorio desde el sistema de control de versiones y recibe la nueva migración. |
||||
4. Doug aplica dicha migración a su base de datos local de desarrollo, de ese modo sincronizando su base de datos |
||||
y reflejando los cambios que hizo Tim. |
||||
|
||||
Los siguientes pasos muestran cómo hacer una puesta en producción con una migración de base de datos: |
||||
|
||||
1. Scott crea un tag de lanzamiento en el repositorio del proyecto que contiene algunas migraciones de base de datos. |
||||
2. Scott actualiza el código fuente en el servidor de producción con el tag de lanzamiento. |
||||
3. Scott aplica cualquier migración de base de datos acumulada a la base de datos de producción. |
||||
|
||||
Yii provee un grupo de herramientas de línea de comandos que te permite: |
||||
|
||||
* crear nuevas migraciones; |
||||
* aplicar migraciones; |
||||
* revertir migraciones; |
||||
* re-aplicar migraciones; |
||||
* mostrar el historial y estado de migraciones. |
||||
|
||||
Todas esas herramientas son accesibles a través del comando `yii migrate`. En esta sección describiremos en detalle |
||||
cómo lograr varias tareas utilizando dichas herramientas. Puedes a su vez ver el uso de cada herramienta a través del comando |
||||
de ayuda `yii help migrate`. |
||||
|
||||
> Tip: las migraciones pueden no sólo afectar un esquema de base de datos sino también ajustar datos existentes para que encajen en el nuevo esquema, crear herencia RBAC |
||||
o también limpiar el cache. |
||||
|
||||
|
||||
## Creando Migraciones <span id="creating-migrations"></span> |
||||
|
||||
Para crear una nueva migración, ejecuta el siguiente comando: |
||||
|
||||
``` |
||||
yii migrate/create <name> |
||||
``` |
||||
|
||||
El argumento requerido `name` da una pequeña descripción de la nueva migración. Por ejemplo, si |
||||
la migración se trata acerca de crear una nueva tabla llamada *news*, podrías utilizar el nombre `create_news_table` |
||||
y ejecutar el siguiente comando: |
||||
|
||||
``` |
||||
yii migrate/create create_news_table |
||||
``` |
||||
|
||||
> Note: Debido a que el argumento `name` será utilizado como parte del nombre de clase de la migración generada, |
||||
sólo debería contener letras, dígitos, y/o guines bajos. |
||||
|
||||
El comando anterior un nuevo archivo de clase PHP llamado `m150101_185401_create_news_table.php` |
||||
en el directorio `@app/migrations`. El archivo contendrá el siguiente código, que principalmente declara |
||||
una clase de tipo migración `m150101_185401_create_news_table` con el siguiente esqueleto de código: |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
use yii\db\Migration; |
||||
|
||||
class m150101_185401_create_news_table extends Migration |
||||
{ |
||||
public function up() |
||||
{ |
||||
|
||||
} |
||||
|
||||
public function down() |
||||
{ |
||||
echo "m101129_185401_create_news_table cannot be reverted.\n"; |
||||
|
||||
return false; |
||||
} |
||||
|
||||
/* |
||||
// Use safeUp/safeDown to run migration code within a transaction |
||||
public function safeUp() |
||||
{ |
||||
} |
||||
|
||||
public function safeDown() |
||||
{ |
||||
} |
||||
*/ |
||||
} |
||||
``` |
||||
|
||||
Cada migración de base de datos es definida como una clase PHP que extiende de [[yii\db\Migration]]. La nombre de clase |
||||
de la migración es generado automáticamente en el formato `m<YYMMDD_HHMMSS>_<Name>`, donde |
||||
|
||||
* `<YYMMDD_HHMMSS>` se refiere a la marca de tiempo UTC en la cual el comando de migración fue ejecutado. |
||||
* `<Name>` es el mismo valor del argumento `name` provisto al ejecutar el comando. |
||||
|
||||
En la clase de la migración, se espera que tu escribas código en el método `up()`, que realiza los cambios en la base de datos. |
||||
Podrías también querer introducir código en el método `down()`, que debería revertir los cambios realizados por `up()`. El método `up()` es llamado |
||||
cuando actualizas la base de datos con esta migración, mientras que el método `down()` es llamado cuando reviertes dicha migración. |
||||
El siguiente código muestra cómo podrías implementar la clase de migración para crear la tabla `news`: |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
use yii\db\Schema; |
||||
use yii\db\Migration; |
||||
|
||||
class m150101_185401_create_news_table extends Migration |
||||
{ |
||||
public function up() |
||||
{ |
||||
$this->createTable('news', [ |
||||
'id' => Schema::TYPE_PK, |
||||
'title' => Schema::TYPE_STRING . ' NOT NULL', |
||||
'content' => Schema::TYPE_TEXT, |
||||
]); |
||||
} |
||||
|
||||
public function down() |
||||
{ |
||||
$this->dropTable('news'); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
> Info: No todas las migraciones son reversibles. Por ejemplo, si el método `up()` elimina un registro en una tabla, podrías |
||||
no ser capáz de recuperarla en el método `down()`. A veces, podrías ser simplemente demasiado perezoso para implementar |
||||
el método `down()`, debido a que no es muy común revertir migraciones de base de datos. En este caso, deberías devolver |
||||
`false` en el método `down()` para indicar que dicha migración no es reversible. |
||||
|
||||
La clase de migración de base de datos [[yii\db\Migration]] expone una conexión a la base de datos mediante la propiedad [[yii\db\Migration::db|db]]. |
||||
Puedes utilizar esto para manipular el esquema de la base de datos utilizando métodos como se describen en |
||||
[Trabajando con Esquemas de Base de Datos](db-dao.md#working-with-database-schema-). |
||||
|
||||
En vez de utilizar tipos físicos, al crear tablas o columnas deberías utilizar los *tipos abstractos* |
||||
así las migraciones son independientes de algún DBMS específico. La clase [[yii\db\Schema]] define |
||||
un grupo de constantes que representan los tipos abstractos soportados. Dichas constantes son llamadas utilizando el formato |
||||
de `TYPE_<Name>`. Por ejemplo, `TYPE_PK` se refiere al tipo clave primaria auto-incremental; `TYPE_STRING` |
||||
se refiere al tipo string. Cuando se aplica una migración a una base de datos en particular, los tipos abstractos |
||||
serán traducidos a los tipos físicos correspondientes. En el caso de MySQL, `TYPE_PK` será transformado |
||||
en `int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY`, mientras `TYPE_STRING` se vuelve `varchar(255)`. |
||||
|
||||
Puedes agregar restricciones adicionales al utilizar tipos abstractos. En el ejemplo anterior, ` NOT NULL` es agregado |
||||
a `Schema::TYPE_STRING` para especificar que la columna no puede ser null. |
||||
|
||||
> Info: El mapeo entre tipos abstractos y tipos físicos es especificado en |
||||
la propiedad [[yii\db\QueryBuilder::$typeMap|$typeMap]] en cada clase concreta `QueryBuilder`. |
||||
|
||||
Desde la versión 2.0.6, puedes hacer uso del recientemente introducido generador de esquemas, el cual provee una forma más conveniente de definir las columnas. |
||||
De esta manera, la migración anterior podría ser escrita así: |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
use yii\db\Migration; |
||||
|
||||
class m150101_185401_create_news_table extends Migration |
||||
{ |
||||
public function up() |
||||
{ |
||||
$this->createTable('news', [ |
||||
'id' => $this->primaryKey(), |
||||
'title' => $this->string()->notNull(), |
||||
'content' => $this->text(), |
||||
]); |
||||
} |
||||
|
||||
public function down() |
||||
{ |
||||
$this->dropTable('news'); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Existe una lista de todos los métodos disponibles para la definición de tipos de columna en la API de la documentación de [[yii\db\SchemaBuilderTrait]]. |
||||
|
||||
|
||||
## Generar Migraciones <span id="generating-migrations"></span> |
||||
|
||||
Desde la versión 2.0.7 la consola provee una manera muy conveniente de generar migraciones. |
||||
|
||||
Si el nombre de la migración tiene una forma especial, por ejemplo `create_xxx` o `drop_xxx` entonces el archivo de la migración generada |
||||
contendrá código extra, en este caso para crear/eliminar tablas. |
||||
A continuación se describen todas estas variantes. |
||||
|
||||
### Crear Tabla |
||||
|
||||
```php |
||||
yii migrate/create create_post |
||||
``` |
||||
|
||||
esto genera |
||||
|
||||
```php |
||||
/** |
||||
* Handles the creation for table `post`. |
||||
*/ |
||||
class m150811_220037_create_post extends Migration |
||||
{ |
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function up() |
||||
{ |
||||
$this->createTable('post', [ |
||||
'id' => $this->primaryKey() |
||||
]); |
||||
} |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function down() |
||||
{ |
||||
$this->dropTable('post'); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Para crear las columnas en ese momento, las puedes especificar vía la opción `--fields`. |
||||
|
||||
```php |
||||
yii migrate/create create_post --fields="title:string,body:text" |
||||
``` |
||||
|
||||
genera |
||||
|
||||
```php |
||||
/** |
||||
* Handles the creation for table `post`. |
||||
*/ |
||||
class m150811_220037_create_post extends Migration |
||||
{ |
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function up() |
||||
{ |
||||
$this->createTable('post', [ |
||||
'id' => $this->primaryKey(), |
||||
'title' => $this->string(), |
||||
'body' => $this->text(), |
||||
]); |
||||
} |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function down() |
||||
{ |
||||
$this->dropTable('post'); |
||||
} |
||||
} |
||||
|
||||
``` |
||||
|
||||
Puedes especificar más parámetros para las columnas. |
||||
|
||||
```php |
||||
yii migrate/create create_post --fields="title:string(12):notNull:unique,body:text" |
||||
``` |
||||
|
||||
genera |
||||
|
||||
```php |
||||
/** |
||||
* Handles the creation for table `post`. |
||||
*/ |
||||
class m150811_220037_create_post extends Migration |
||||
{ |
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function up() |
||||
{ |
||||
$this->createTable('post', [ |
||||
'id' => $this->primaryKey(), |
||||
'title' => $this->string(12)->notNull()->unique(), |
||||
'body' => $this->text() |
||||
]); |
||||
} |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function down() |
||||
{ |
||||
$this->dropTable('post'); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
> Note: la clave primaria es automáticamente agragada y llamada `id` por defecto. Si quieres utilizar otro nombre puedes |
||||
> especificarlo así `--fields="name:primaryKey"`. |
||||
|
||||
#### Claves Foráneas |
||||
|
||||
Desde 2.0.8 el generador soporta claves foráneas utilizando la palabra clave `foreignKey`. |
||||
|
||||
```php |
||||
yii migrate/create create_post --fields="author_id:integer:notNull:foreignKey(user),category_id:integer:defaultValue(1):foreignKey,title:string,body:text" |
||||
``` |
||||
|
||||
genera |
||||
|
||||
```php |
||||
/** |
||||
* Handles the creation for table `post`. |
||||
* Has foreign keys to the tables: |
||||
* |
||||
* - `user` |
||||
* - `category` |
||||
*/ |
||||
class m160328_040430_create_post extends Migration |
||||
{ |
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function up() |
||||
{ |
||||
$this->createTable('post', [ |
||||
'id' => $this->primaryKey(), |
||||
'author_id' => $this->integer()->notNull(), |
||||
'category_id' => $this->integer()->defaultValue(1), |
||||
'title' => $this->string(), |
||||
'body' => $this->text(), |
||||
]); |
||||
|
||||
// creates index for column `author_id` |
||||
$this->createIndex( |
||||
'idx-post-author_id', |
||||
'post', |
||||
'author_id' |
||||
); |
||||
|
||||
// add foreign key for table `user` |
||||
$this->addForeignKey( |
||||
'fk-post-author_id', |
||||
'post', |
||||
'author_id', |
||||
'user', |
||||
'id', |
||||
'CASCADE' |
||||
); |
||||
|
||||
// creates index for column `category_id` |
||||
$this->createIndex( |
||||
'idx-post-category_id', |
||||
'post', |
||||
'category_id' |
||||
); |
||||
|
||||
// add foreign key for table `category` |
||||
$this->addForeignKey( |
||||
'fk-post-category_id', |
||||
'post', |
||||
'category_id', |
||||
'category', |
||||
'id', |
||||
'CASCADE' |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function down() |
||||
{ |
||||
// drops foreign key for table `user` |
||||
$this->dropForeignKey( |
||||
'fk-post-author_id', |
||||
'post' |
||||
); |
||||
|
||||
// drops index for column `author_id` |
||||
$this->dropIndex( |
||||
'idx-post-author_id', |
||||
'post' |
||||
); |
||||
|
||||
// drops foreign key for table `category` |
||||
$this->dropForeignKey( |
||||
'fk-post-category_id', |
||||
'post' |
||||
); |
||||
|
||||
// drops index for column `category_id` |
||||
$this->dropIndex( |
||||
'idx-post-category_id', |
||||
'post' |
||||
); |
||||
|
||||
$this->dropTable('post'); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
La posición de la palabra clave `foreignKey` en la descripción de la columna |
||||
no cambia el código generado. Esto significa: |
||||
|
||||
- `author_id:integer:notNull:foreignKey(user)` |
||||
- `author_id:integer:foreignKey(user):notNull` |
||||
- `author_id:foreignKey(user):integer:notNull` |
||||
|
||||
Todas generan el mismo código. |
||||
|
||||
La palabra clave `foreignKey` puede tomar un parámetro entre paréntesis el cual |
||||
será el nombre de la tabla relacionada por la clave foránea generada. Si no se pasa ningún parámetro |
||||
el nombre de la tabla será deducido en base al nombre de la columna. |
||||
|
||||
En el ejemplo anterior `author_id:integer:notNull:foreignKey(user)` generará |
||||
una columna llamada `author_id` con una clave foránea a la tabla `user` mientras |
||||
`category_id:integer:defaultValue(1):foreignKey` generará |
||||
`category_id` con una clave foránea a la tabla `category`. |
||||
|
||||
### Eliminar Tabla |
||||
|
||||
```php |
||||
yii migrate/create drop_post --fields="title:string(12):notNull:unique,body:text" |
||||
``` |
||||
|
||||
genera |
||||
|
||||
```php |
||||
class m150811_220037_drop_post extends Migration |
||||
{ |
||||
public function up() |
||||
{ |
||||
$this->dropTable('post'); |
||||
} |
||||
|
||||
public function down() |
||||
{ |
||||
$this->createTable('post', [ |
||||
'id' => $this->primaryKey(), |
||||
'title' => $this->string(12)->notNull()->unique(), |
||||
'body' => $this->text() |
||||
]); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
### Agregar Columna |
||||
|
||||
Si el nombre de la migración está en la forma `add_xxx_to_yyy` entonces el archivo generado contendrá |
||||
las declaraciones `addColumn` y `dropColumn` necesarias. |
||||
|
||||
Para agregar una columna: |
||||
|
||||
```php |
||||
yii migrate/create add_position_to_post --fields="position:integer" |
||||
``` |
||||
|
||||
genera |
||||
|
||||
```php |
||||
class m150811_220037_add_position_to_post extends Migration |
||||
{ |
||||
public function up() |
||||
{ |
||||
$this->addColumn('post', 'position', $this->integer()); |
||||
} |
||||
|
||||
public function down() |
||||
{ |
||||
$this->dropColumn('post', 'position'); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
### Eliminar Columna |
||||
|
||||
Si el nombre de la migración está en la forma `drop_xxx_from_yyy` entonces el archivo generado contendrá |
||||
las declaraciones `addColumn` y `dropColumn` necesarias. |
||||
|
||||
```php |
||||
yii migrate/create drop_position_from_post --fields="position:integer" |
||||
``` |
||||
|
||||
genera |
||||
|
||||
```php |
||||
class m150811_220037_drop_position_from_post extends Migration |
||||
{ |
||||
public function up() |
||||
{ |
||||
$this->dropColumn('post', 'position'); |
||||
} |
||||
|
||||
public function down() |
||||
{ |
||||
$this->addColumn('post', 'position', $this->integer()); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
### Agregar Tabla de Unión |
||||
|
||||
Si el nombre de la migración está en la forma `create_junction_xxx_and_yyy` entonces se generará el código necesario |
||||
para una tabla de unión. |
||||
|
||||
```php |
||||
yii migrate/create create_junction_post_and_tag --fields="created_at:dateTime" |
||||
``` |
||||
|
||||
genera |
||||
|
||||
```php |
||||
/** |
||||
* Handles the creation for table `post_tag`. |
||||
* Has foreign keys to the tables: |
||||
* |
||||
* - `post` |
||||
* - `tag` |
||||
*/ |
||||
class m160328_041642_create_junction_post_and_tag extends Migration |
||||
{ |
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function up() |
||||
{ |
||||
$this->createTable('post_tag', [ |
||||
'post_id' => $this->integer(), |
||||
'tag_id' => $this->integer(), |
||||
'created_at' => $this->dateTime(), |
||||
'PRIMARY KEY(post_id, tag_id)', |
||||
]); |
||||
|
||||
// creates index for column `post_id` |
||||
$this->createIndex( |
||||
'idx-post_tag-post_id', |
||||
'post_tag', |
||||
'post_id' |
||||
); |
||||
|
||||
// add foreign key for table `post` |
||||
$this->addForeignKey( |
||||
'fk-post_tag-post_id', |
||||
'post_tag', |
||||
'post_id', |
||||
'post', |
||||
'id', |
||||
'CASCADE' |
||||
); |
||||
|
||||
// creates index for column `tag_id` |
||||
$this->createIndex( |
||||
'idx-post_tag-tag_id', |
||||
'post_tag', |
||||
'tag_id' |
||||
); |
||||
|
||||
// add foreign key for table `tag` |
||||
$this->addForeignKey( |
||||
'fk-post_tag-tag_id', |
||||
'post_tag', |
||||
'tag_id', |
||||
'tag', |
||||
'id', |
||||
'CASCADE' |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @inheritdoc |
||||
*/ |
||||
public function down() |
||||
{ |
||||
// drops foreign key for table `post` |
||||
$this->dropForeignKey( |
||||
'fk-post_tag-post_id', |
||||
'post_tag' |
||||
); |
||||
|
||||
// drops index for column `post_id` |
||||
$this->dropIndex( |
||||
'idx-post_tag-post_id', |
||||
'post_tag' |
||||
); |
||||
|
||||
// drops foreign key for table `tag` |
||||
$this->dropForeignKey( |
||||
'fk-post_tag-tag_id', |
||||
'post_tag' |
||||
); |
||||
|
||||
// drops index for column `tag_id` |
||||
$this->dropIndex( |
||||
'idx-post_tag-tag_id', |
||||
'post_tag' |
||||
); |
||||
|
||||
$this->dropTable('post_tag'); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
### Migraciones Transaccionales <span id="transactional-migrations"></span> |
||||
|
||||
Al ejecutar migraciones complejas de BD, es importante asegurarse que todas las migraciones funcionen o fallen como una unidad |
||||
así la base de datos puede mantener integridad y consistencia. Para alcanzar este objetivo, se recomienda que |
||||
encierres las operación de la BD de cada migración en una [transacción](db-dao.md#performing-transactions). |
||||
|
||||
Una manera simple de implementar migraciones transaccionales es poniendo el código de las migraciones en los métodos `safeUp()` y `safeDown()`. |
||||
Estos métodos se diferencias con `up()` y `down()` en que son encerrados implícitamente en una transacción. |
||||
Como resultado, si alguna de las operaciones dentro de estos métodos falla, todas las operaciones previas son automáticamente revertidas. |
||||
|
||||
En el siguiente ejemplo, además de crear la tabla `news` también insertamos un registro inicial dentro de la dicha tabla. |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
use yii\db\Migration; |
||||
|
||||
class m150101_185401_create_news_table extends Migration |
||||
{ |
||||
public function safeUp() |
||||
{ |
||||
$this->createTable('news', [ |
||||
'id' => $this->primaryKey(), |
||||
'title' => $this->string()->notNull(), |
||||
'content' => $this->text(), |
||||
]); |
||||
|
||||
$this->insert('news', [ |
||||
'title' => 'test 1', |
||||
'content' => 'content 1', |
||||
]); |
||||
} |
||||
|
||||
public function safeDown() |
||||
{ |
||||
$this->delete('news', ['id' => 1]); |
||||
$this->dropTable('news'); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Ten en cuenta que usualmente cuando ejecutas múltiples operaciones en la BD en `safeUp()`, deberías revertir su orden de ejecución |
||||
en `safeDown()`. En el ejemplo anterior primero creamos la tabla y luego insertamos la finla en `safeUp()`; mientras |
||||
que en `safeDown()` primero eliminamos el registro y posteriormente eliminamos la tabla. |
||||
|
||||
> Note: No todos los DBMS soportan transacciones. Y algunas consultas a la BD no pueden ser puestas en transacciones. Para algunos ejemplos, |
||||
por favor lee acerca de [commits implícitos](http://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html). En estos casos, |
||||
deberías igualmente implementar `up()` y `down()`. |
||||
|
||||
|
||||
### Métodos de Acceso a la Base de Datos <span id="db-accessing-methods"></span> |
||||
|
||||
La clase base [[yii\db\Migration]] provee un grupo de métodos que te permiten acceder y manipular bases de datos. |
||||
Podrías encontrar que estos métodos son nombrados de forma similar a los [métodos DAO](db-dao.md) provistos por la clase [[yii\db\Command]]. |
||||
Por ejemplo, el método [[yii\db\Migration::createTable()]] te permite crear una nueva tabla, |
||||
tal como lo hace [[yii\db\Command::createTable()]]. |
||||
|
||||
El beneficio de utilizar lo métodos provistos por [[yii\db\Migration]] es que no necesitas explícitamente |
||||
crear instancias de [[yii\db\Command]], y la ejecución de cada método mostrará automáticamente mensajes útiles |
||||
diciéndote qué operaciones de la base de datos se realizaron y cuánto tiempo tomaron. |
||||
|
||||
Debajo hay una lista de todos los métodos de acceso a la base de datos: |
||||
|
||||
* [[yii\db\Migration::execute()|execute()]]: ejecuta una declaración SQL |
||||
* [[yii\db\Migration::insert()|insert()]]: inserta un único registro |
||||
* [[yii\db\Migration::batchInsert()|batchInsert()]]: inserta múltiples registros |
||||
* [[yii\db\Migration::update()|update()]]: actualiza registros |
||||
* [[yii\db\Migration::delete()|delete()]]: elimina registros |
||||
* [[yii\db\Migration::createTable()|createTable()]]: crea una nueva tabla |
||||
* [[yii\db\Migration::renameTable()|renameTable()]]: renombra una tabla |
||||
* [[yii\db\Migration::dropTable()|dropTable()]]: elimina una tabla |
||||
* [[yii\db\Migration::truncateTable()|truncateTable()]]: elimina todos los registros de una tabla |
||||
* [[yii\db\Migration::addColumn()|addColumn()]]: agrega una columna |
||||
* [[yii\db\Migration::renameColumn()|renameColumn()]]: renombra una columna |
||||
* [[yii\db\Migration::dropColumn()|dropColumn()]]: elimina una columna |
||||
* [[yii\db\Migration::alterColumn()|alterColumn()]]: modifica una columna |
||||
* [[yii\db\Migration::addPrimaryKey()|addPrimaryKey()]]: agrega una clave primaria |
||||
* [[yii\db\Migration::dropPrimaryKey()|dropPrimaryKey()]]: elimina una clave primaria |
||||
* [[yii\db\Migration::addForeignKey()|addForeignKey()]]: agrega una clave foránea |
||||
* [[yii\db\Migration::dropForeignKey()|dropForeignKey()]]: elimina una clave foránea |
||||
* [[yii\db\Migration::createIndex()|createIndex()]]: crea un índice |
||||
* [[yii\db\Migration::dropIndex()|dropIndex()]]: elimina un índice |
||||
* [[yii\db\Migration::addCommentOnColumn()|addCommentOnColumn()]: agrega un comentario a una columna |
||||
* [[yii\db\Migration::dropCommentFromColumn()|dropCommentFromColumn()]: elimina un comentario de una columna |
||||
* [[yii\db\Migration::addCommentOnTable()|addCommentOnTable()]: agrega un comentario a una tabla |
||||
* [[yii\db\Migration::dropCommentFromTable()|dropCommentFromTable()]: elimina un comentario de una tabla |
||||
|
||||
> Info: [[yii\db\Migration]] no provee un método de consulta a la base de datos. Esto es porque normalmente no necesitas |
||||
mostrar mensajes detallados al traer datos de una base de datos. También se debe a que puedes utilizar el poderoso |
||||
[Query Builder](db-query-builder.md) para generar y ejecutar consultas complejas. |
||||
|
||||
> Note: Al manipular datos utilizando una migración podrías encontrar que utilizando tus clases [Active Record](db-active-record.md) |
||||
> para esto podría ser útil ya que algo de la lógica ya está implementada ahí. Ten en cuenta de todos modos, que en contraste con |
||||
> el código escrito en las migraciones, cuya naturaleza es permanecer constante por siempre, la lógica de la aplicación está sujeta a cambios. |
||||
> Entonces al utilizar Active Record en migraciones, los cambios en la lógica en la capa Active Record podrían accidentalmente romper |
||||
> migraciones existentes. Por esta razón, el código de las migraciones debería permanecer independiente de determinada lógica de la aplicación |
||||
> tal como clases Active Record. |
||||
|
||||
|
||||
## Aplicar Migraciones <span id="applying-migrations"></span> |
||||
|
||||
To upgrade a database to its latest structure, you should apply all available new migrations using the following command: |
||||
Para actualizar una base de datos a su última estructura, deberías aplicar todas las nuevas migraciones utilizando el siguiente comando: |
||||
|
||||
``` |
||||
yii migrate |
||||
``` |
||||
|
||||
Este comando listará todas las migraciones que no han sido aplicadas hasta el momento. Si confirmas que quieres aplicar |
||||
dichas migraciones, se correrá el método `up()` o `safeUp()` en cada clase de migración nueva, una tras otra, |
||||
en el orden de su valor de marca temporal. Si alguna de las migraciones falla, el comando terminará su ejecución sin aplicar |
||||
el resto de las migraciones. |
||||
|
||||
> Tip: En caso de no disponer de la línea de comandos en el servidor, podrías intentar utilizar |
||||
> la extensión [web shell](https://github.com/samdark/yii2-webshell). |
||||
|
||||
Por cada migración aplicada correctamente, el comando insertará un registro en la base de datos, en la tabla llamada |
||||
`migration` para registrar la correcta aplicación de la migración. Esto permitirá a la herramienta de migración identificar |
||||
cuáles migraciones han sido aplicadas y cuáles no. |
||||
|
||||
> Info: La herramienta de migración creará automáticamente la tabla `migration` en la base de datos especificada |
||||
en la opción [[yii\console\controllers\MigrateController::db|db]] del comando. Por defecto, la base de datos |
||||
es especificada en el [componente de aplicación](structure-application-components.md) `db`. |
||||
|
||||
A veces, podrías sólo querer aplicar una o algunas pocas migraciones, en vez de todas las migraciones disponibles. |
||||
Puedes hacer esto el número de migraciones que quieres aplicar al ejecutar el comando. |
||||
Por ejemplo, el siguiente comando intentará aplicar las tres siguientes migraciones disponibles: |
||||
|
||||
``` |
||||
yii migrate 3 |
||||
``` |
||||
|
||||
Puedes además explícitamente especificar una migración en particular a la cual la base de datos debería migrar |
||||
utilizando el comando `migrate/to` de acuerdo a uno de los siguientes formatos: |
||||
|
||||
``` |
||||
yii migrate/to 150101_185401 # utiliza la marca temporal para especificar la migración |
||||
yii migrate/to "2015-01-01 18:54:01" # utiliza un string que puede ser analizado por strtotime() |
||||
yii migrate/to m150101_185401_create_news_table # utiliza el nombre completo |
||||
yii migrate/to 1392853618 # utiliza el tiempo UNIX |
||||
``` |
||||
|
||||
Si hubiera migraciones previas a la especificada sin aplicar, estas serán aplicadas antes de que la migración especificada |
||||
sea aplicada. |
||||
|
||||
Si la migración especificada ha sido aplicada previamente, cualquier migración aplicada posteriormente será revertida. |
||||
|
||||
|
||||
## Revertir Migraciones <span id="reverting-migrations"></span> |
||||
|
||||
Para revertir (deshacer) una o varias migraciones ya aplicadas, puedes ejecutar el siguiente comando: |
||||
|
||||
``` |
||||
yii migrate/down # revierte la más reciente migración aplicada |
||||
yii migrate/down 3 # revierte las 3 últimas migraciones aplicadas |
||||
``` |
||||
|
||||
> Note: No todas las migraciones son reversibles. Intentar revertir tales migraciones producirá un error y detendrá |
||||
completamente el proceso de reversión. |
||||
|
||||
|
||||
## Rehacer Migraciones <span id="redoing-migrations"></span> |
||||
|
||||
Rehacer (re-ejecutar) migraciones significa primero revertir las migraciones especificadas y luego aplicarlas nuevamente. Esto puede hacerse |
||||
de esta manera: |
||||
|
||||
``` |
||||
yii migrate/redo # rehace la más reciente migración aplicada |
||||
yii migrate/redo 3 # rehace las 3 últimas migraciones aplicadas |
||||
``` |
||||
|
||||
> Note: Si una migración no es reversible, no tendrás posibilidades de rehacerla. |
||||
|
||||
|
||||
## Listar Migraciones <span id="listing-migrations"></span> |
||||
|
||||
Para listar cuáles migraciones han sido aplicadas y cuáles no, puedes utilizar los siguientes comandos: |
||||
|
||||
``` |
||||
yii migrate/history # muestra las últimas 10 migraciones aplicadas |
||||
yii migrate/history 5 # muestra las últimas 5 migraciones aplicadas |
||||
yii migrate/history all # muestra todas las migraciones aplicadas |
||||
|
||||
yii migrate/new # muestra las primeras 10 nuevas migraciones |
||||
yii migrate/new 5 # muestra las primeras 5 nuevas migraciones |
||||
yii migrate/new all # muestra todas las nuevas migraciones |
||||
``` |
||||
|
||||
|
||||
## Modificar el Historial de Migraciones <span id="modifying-migration-history"></span> |
||||
|
||||
En vez de aplicar o revertir migraciones, a veces simplemente quieres marcar que tu base de datos |
||||
ha sido actualizada a una migración en particular. Esto sucede normalmente cuando cambias manualmente la base de datos |
||||
a un estado particular y no quieres que la/s migración/es de ese cambio sean re-aplicadas posteriormente. Puedes alcanzar este objetivo |
||||
con el siguiente comando: |
||||
|
||||
``` |
||||
yii migrate/mark 150101_185401 # utiliza la marca temporal para especificar la migración |
||||
yii migrate/mark "2015-01-01 18:54:01" # utiliza un string que puede ser analizado por strtotime() |
||||
yii migrate/mark m150101_185401_create_news_table # utiliza el nombre completo |
||||
yii migrate/mark 1392853618 # utiliza el tiempo UNIX |
||||
``` |
||||
|
||||
El comando modificará la tabla `migration` agregando o eliminado ciertos registros para indicar que en la base de datos |
||||
han sido aplicadas las migraciones hasta la especificada. Ninguna migración será aplicada ni revertida por este comando. |
||||
|
||||
|
||||
## Personalizar Migraciones <span id="customizing-migrations"></span> |
||||
|
||||
Hay varias maneras de personalizar el comando de migración. |
||||
|
||||
|
||||
### Utilizar Opciones de la Línea de Comandos <span id="using-command-line-options"></span> |
||||
|
||||
El comando de migración trae algunas opciones de línea de comandos que pueden ser utilizadas para personalizar su comportamiento: |
||||
|
||||
* `interactive`: boolean (por defecto true), especificar si se debe ejecutar la migración en modo interactivo. |
||||
Cuando se indica true, se le pedirá confirmación al usuario antes de ejecutar ciertas acciones. |
||||
Puedes querer definirlo como false si el comando está siendo utilizado como un proceso de fondo. |
||||
|
||||
* `migrationPath`: string (por defecto `@app/migrations`), especifica el directorio que contiene todos los archivos |
||||
de clase de las migraciones. Este puede ser especificado tanto como una ruta a un directorio un [alias](concept-aliases.md) de ruta. |
||||
Ten en cuenta que el directorio debe existir, o el comando disparará un error. |
||||
|
||||
* `migrationTable`: string (por defecto `migration`), especifica el nombre de la tabla de la base de datos que almacena |
||||
información del historial de migraciones. Dicha tabla será creada por el comando en caso de que no exista. |
||||
Puedes también crearla manualmente utilizando la estructura `version varchar(255) primary key, apply_time integer`. |
||||
|
||||
* `db`: string (por defecto `db`), especifica el ID del [componente de aplicación](structure-application-components.md) de la base de datos. |
||||
Esto representa la base de datos que será migrada en este comando. |
||||
|
||||
* `templateFile`: string (por defecto `@yii/views/migration.php`), especifica la ruta al template |
||||
utilizado para generar el esqueleto de los archivos de clases de migración. Puede ser especificado tanto como una ruta a un archivo |
||||
como una [alias](concept-aliases.md) de una ruta. El template es un archivo PHP en el cual puedes utilizar una variable predefinida |
||||
llamada `$className` para obtener el nombre de clase de la migración. |
||||
|
||||
* `generatorTemplateFiles`: array (por defecto `[ |
||||
'create_table' => '@yii/views/createTableMigration.php', |
||||
'drop_table' => '@yii/views/dropTableMigration.php', |
||||
'add_column' => '@yii/views/addColumnMigration.php', |
||||
'drop_column' => '@yii/views/dropColumnMigration.php', |
||||
'create_junction' => '@yii/views/createJunctionMigration.php' |
||||
]`), especifica los templates utilizados para generar las migraciones. Ver "[Generar Migraciones](#generating-migrations)" |
||||
para más detalles. |
||||
|
||||
* `fields`: array de strings de definiciones de columna utilizado por el código de migración. Por defecto `[]`. El formato de cada |
||||
definición es `COLUMN_NAME:COLUMN_TYPE:COLUMN_DECORATOR`. Por ejemplo, `--fields=name:string(12):notNull` produce |
||||
una columna string de tamaño 12 que es not null. |
||||
|
||||
El siguiente ejemplo muestra cómo se pueden utilizar estas opciones. |
||||
|
||||
Por ejemplo, si queremos migrar un módulo `forum` cuyos arhivos de migración |
||||
están ubicados dentro del directorio `migrations` del módulo, podemos utilizar el siguientedocs/guide-es/db-migrations.md |
||||
comando: |
||||
|
||||
``` |
||||
# realiza las migraciones de un módulo forum sin interacción del usuario |
||||
yii migrate --migrationPath=@app/modules/forum/migrations --interactive=0 |
||||
``` |
||||
|
||||
|
||||
### Configurar el Comando Globalmente <span id="configuring-command-globally"></span> |
||||
|
||||
En vez de introducir los valores de las opciones cada vez que ejecutas un comandod e migración, podrías configurarlos |
||||
de una vez por todas en la configuración de la aplicación como se muestra a continuación: |
||||
|
||||
```php |
||||
return [ |
||||
'controllerMap' => [ |
||||
'migrate' => [ |
||||
'class' => 'yii\console\controllers\MigrateController', |
||||
'migrationTable' => 'backend_migration', |
||||
], |
||||
], |
||||
]; |
||||
``` |
||||
|
||||
Con esta configuración, cada vez que ejecutes un comando de migración, la tabla `backend_migration` |
||||
será utilizada para registrar el historial de migraciones. No necesitarás volver a especificarla con la opción `migrationTable` |
||||
de la línea de comandos. |
||||
|
||||
|
||||
## Migrar Múltiples Bases de Datos <span id="migrating-multiple-databases"></span> |
||||
|
||||
Por defecto, las migraciones son aplicadas en la misma base de datos especificada en el [componente de aplicación](structure-application-components.md) `db`. |
||||
Si quieres que sean aplicadas en una base de datos diferente, puedes especificar la opción `db` como se muestra a continuación, |
||||
|
||||
``` |
||||
yii migrate --db=db2 |
||||
``` |
||||
|
||||
El comando anterior aplicará las migraciones en la base de datos `db2`. |
||||
|
||||
A veces puede suceder que quieras aplicar *algunas* de las migraciones a una base de datos, mientras algunas otras |
||||
a una base de datos distinta. Para lograr esto, al implementar una clase de migración debes especificar explícitamente el ID del componente DB |
||||
que la migración debe utilizar, como a continuación: |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
use yii\db\Migration; |
||||
|
||||
class m150101_185401_create_news_table extends Migration |
||||
{ |
||||
public function init() |
||||
{ |
||||
$this->db = 'db2'; |
||||
parent::init(); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
La migración anterior se aplicará a `db2`, incluso si especificas una base de datos diferente en la opción `db` de la |
||||
línea de comandos. Ten en cuenta que el historial aún será registrado in la base de datos especificada en la opción `db` de la línea de comandos. |
||||
|
||||
Si tienes múltiples migraciones que utilizan la misma base de datos, es recomandable que crees una clase base de migración |
||||
con el código `init()` mostrado. Entonces cada clase de migración puede extender de esa clase base. |
||||
|
||||
> Tip: Aparte de definir la propiedad [[yii\db\Migration::db|db]], puedes también operar en diferentes bases de datos |
||||
creando nuevas conexiones de base de datos en tus clases de migración. También puedes utilizar [métodos DAO](db-dao.md) |
||||
con esas conexiones para manipular diferentes bases de datos. |
||||
|
||||
Another strategy that you can take to migrate multiple databases is to keep migrations for different databases in |
||||
different migration paths. Then you can migrate these databases in separate commands like the following: |
||||
Otra estrategia que puedes seguir para migrar múltiples bases de datos es mantener las migraciones para diferentes bases de datos en |
||||
distintas rutas de migración. Entonces podrías migrar esas bases de datos en comandos separados como a continuación: |
||||
|
||||
``` |
||||
yii migrate --migrationPath=@app/migrations/db1 --db=db1 |
||||
yii migrate --migrationPath=@app/migrations/db2 --db=db2 |
||||
... |
||||
``` |
||||
|
||||
El primer comando aplicará las migraciones que se encuentran en `@app/migrations/db1` en la base de datos `db1`, el segundo comando |
||||
aplicará las migraciones que se encuentran en `@app/migrations/db2` en `db2`, y así sucesivamente. |
@ -0,0 +1,67 @@
|
||||
# A |
||||
|
||||
## alias |
||||
|
||||
Alias es un string utilizado por Yii para referirse a una clase o directorio tal como `@app/vendor`. |
||||
|
||||
## aplicación |
||||
|
||||
La aplicación es el objeto central durante la solicitud HTTP. Contiene un número de componentes con los que toma información de la solicitud y la envía al controlador apropiado para posterior procesamiento. |
||||
|
||||
El objeto de la aplicación es instanciado como un singleton por el script de entrada. El singleton de la aplicación puede ser accedido desde cualquier lugar a través de `\Yii::$app`. |
||||
|
||||
## assets |
||||
|
||||
Asset se refiere a un archivo de recurso. Típicamente contiene JavaScript o CSS pero puede ser cualquier otra cosa que sea accesible vía HTTP. |
||||
|
||||
## atributo |
||||
|
||||
Un atributo es una propiedad de un modelo (una variable miembro de clase o una propiedad mágica definida vía `__get()`/`__set()`) que almacena **datos de negocio**. |
||||
|
||||
# B |
||||
|
||||
## bundle |
||||
|
||||
Bundle, conocido como paquete en Yii 1.1, se refiere a un número de recursos y un archivo de configuración que describe dependencias y lista recursos. |
||||
|
||||
# C |
||||
|
||||
## configuración |
||||
|
||||
Configuración puede referirse tanto al proceso de establecer propiedades de un objeto como a un archivo de configuración que almacena la definición de propiedades para un objeto o clase de objetos. |
||||
|
||||
# E |
||||
|
||||
## extensión |
||||
|
||||
Extensión es un grupo de clases, paquete de recursos y configuraciones que agrega más características a la aplicación. |
||||
|
||||
# I |
||||
|
||||
## instalación |
||||
|
||||
Instalación es el proceso de preparar algo para trabajar, desde seguir un archivo léame hasta ejecutar un script preparado especialmente para tal fin. En el caso de Yii, define permisos y chequea los requerimientos para el funcionamiento del software. |
||||
|
||||
# M |
||||
|
||||
## módulo |
||||
|
||||
Módulo es una sub-aplicación que contiene elementos MVC en sí mismo, como modelos, vistas, controladores, etc. y puede ser utilizado dentro de la aplicación principal. Típicamente remitiendo las solicitudes al módulo en vez de manejándolo desde controladores. |
||||
|
||||
# N |
||||
|
||||
## namespace |
||||
|
||||
Namespace (espacio de nombres) se refiere a una [característica de PHP](http://php.net/manual/es/language.namespaces.php) activamente utilizada en Yii 2. |
||||
|
||||
# P |
||||
|
||||
## paquete |
||||
|
||||
[Ver bundle](#bundle). |
||||
|
||||
# V |
||||
|
||||
## vendor |
||||
|
||||
Vendor (proveedor) es una organización o un desarrollador individual que provee código en forma de extensiones, módulos o librerías. |
@ -0,0 +1,527 @@
|
||||
<?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 attr.name="Description" attr.type="string" for="graph" id="d0"/> |
||||
<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 for="graphml" id="d7" yfiles.type="resources"/> |
||||
<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="d0"/> |
||||
<node id="n0" yfiles.foldertype="group"> |
||||
<data key="d4"/> |
||||
<data key="d6"> |
||||
<y:ProxyAutoBoundsNode> |
||||
<y:Realizers active="0"> |
||||
<y:GroupNode> |
||||
<y:Geometry height="571.4472707112631" width="763.2772213171534" x="-1269.9373595143054" y="-207.17439524332679"/> |
||||
<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="22.37646484375" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="763.2772213171534" x="0.0" y="0.0">Entry script (index.php or 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="22.37646484375" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="59.02685546875" x="-4.513427734375" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="126.759765625" x="99.08306136604676" y="5.6494140625">Load application config<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="d5"/> |
||||
<data key="d6"> |
||||
<y:ProxyAutoBoundsNode> |
||||
<y:Realizers active="0"> |
||||
<y:GroupNode> |
||||
<y:Geometry height="309.37646484374994" width="330.35133296005984" x="-1254.9373595143054" y="35.272875467936274"/> |
||||
<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="22.37646484375" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="330.35133296005984" x="0.0" y="0.0">Create application instance</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="22.37646484375" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="59.02685546875" x="-4.513427734375" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="45.34375" x="124.79106917854676" y="5.6494140625">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.701171875" modelName="custom" textColor="#000000" visible="true" width="120.71875" x="87.10356917854676" y="5.6494140625">Register error handler<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.701171875" modelName="custom" textColor="#000000" visible="true" width="173.435546875" x="60.74517074104676" y="5.6494140625">Configure application properties<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.701171875" modelName="custom" textColor="#000000" visible="true" width="27.33203125" x="133.79692855354676" y="5.6494140625">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.701171875" modelName="custom" textColor="#000000" visible="true" width="62.025390625" x="116.45024886604676" y="5.6494140625">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="d5"/> |
||||
<data key="d6"> |
||||
<y:ProxyAutoBoundsNode> |
||||
<y:Realizers active="0"> |
||||
<y:GroupNode> |
||||
<y:Geometry height="411.6943410237631" width="324.9258883570935" x="-846.5860265542456" y="-169.79793039957679"/> |
||||
<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="22.37646484375" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="324.9258883570935" x="0.0" y="0.0">Run application</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="22.37646484375" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="59.02685546875" x="-4.513427734375" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="164.705078125" x="65.11040511604676" y="5.6494140625">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="d5"/> |
||||
<data key="d6"> |
||||
<y:ProxyAutoBoundsNode> |
||||
<y:Realizers active="0"> |
||||
<y:GroupNode> |
||||
<y:Geometry height="204.37646484375" width="294.9258883570935" x="-831.5860265542456" y="-78.79793039957679"/> |
||||
<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="22.37646484375" modelName="internal" modelPosition="t" textColor="#FFFFFF" visible="true" width="294.9258883570935" x="0.0" y="0.0">Handle request</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="22.37646484375" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="59.02685546875" x="-4.513427734375" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="231.4609375" x="16.732475428546763" y="5.6494140625">Resolve request into route and parameters<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.701171875" modelName="custom" textColor="#000000" visible="true" width="197.44140625" x="33.74224105354676" y="5.6494140625">Create module, controller and action<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.701171875" modelName="custom" textColor="#000000" visible="true" width="61.369140625" x="101.77837386604676" y="5.649414062500057">Run action<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.701171875" modelName="custom" textColor="#000000" visible="true" width="154.697265625" x="70.11431136604676" y="5.6494140625">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.701171875" modelName="custom" textColor="#000000" visible="true" width="148.099609375" x="73.41313949104676" y="5.6494140625">Send response to end user<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.701171875" modelName="custom" textColor="#000000" visible="true" width="160.08203125" x="82.42192855354676" y="5.6494140625">Complete request processing<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="d9"/> |
||||
<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.701171875" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="106.052734375" x="-130.76131968438426" y="78.18481445312506">Configuration array<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="d9"/> |
||||
<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="d9"/> |
||||
<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="d9"/> |
||||
<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="d9"/> |
||||
<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="d9"/> |
||||
<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="d9"/> |
||||
<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="d9"/> |
||||
<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="d9"/> |
||||
<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="d9"/> |
||||
<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="d9"/> |
||||
<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="d9"/> |
||||
<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.701171875" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="59.353515625" x="-93.67673227804266" y="28.318773905436274">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="d7"> |
||||
<y:Resources/> |
||||
</data> |
||||
</graphml> |
After Width: | Height: | Size: 31 KiB |
@ -0,0 +1,368 @@
|
||||
<?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.12.2--> |
||||
<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"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="34.0" y="-11.5"/> |
||||
<y:Fill color="#ADF4A6" transparent="false"/> |
||||
<y:BorderStyle color="#FF0000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" modelName="custom" textColor="#000000" visible="true" width="36.68359375" x="33.158203125" y="25.1494140625">admin<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n1"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="193.0" y="-11.5"/> |
||||
<y:Fill color="#ADF4A6" transparent="false"/> |
||||
<y:BorderStyle color="#FF0000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" modelName="custom" textColor="#000000" visible="true" width="38.025390625" x="32.4873046875" y="25.1494140625">author<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n2"> |
||||
<data key="d6"> |
||||
<y:SVGNode> |
||||
<y:Geometry height="64.53585815429688" width="56.560157775878906" x="216.21992111206055" y="-132.03585815429688"/> |
||||
<y:Fill color="#CCCCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="62.37109375" x="-2.905467987060547" y="-27.814727783203125">John, ID=2<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="-0.5" offsetX="0.0" offsetY="-9.113555908203125" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:SVGNodeProperties usingVisualBounds="true"/> |
||||
<y:SVGModel svgBoundsPolicy="0"> |
||||
<y:SVGContent refid="1"/> |
||||
</y:SVGModel> |
||||
</y:SVGNode> |
||||
</data> |
||||
</node> |
||||
<node id="n3"> |
||||
<data key="d6"> |
||||
<y:SVGNode> |
||||
<y:Geometry height="66.76200103759766" width="56.554100036621094" x="57.22294998168945" y="-133.14892959594727"/> |
||||
<y:Fill color="#CCCCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="62.37109375" x="-2.908496856689453" y="-27.701656341552734">Jane, ID=1<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="-0.5" offsetX="0.0" offsetY="-9.000484466552734" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:SVGNodeProperties usingVisualBounds="true"/> |
||||
<y:SVGModel svgBoundsPolicy="0"> |
||||
<y:SVGContent refid="2"/> |
||||
</y:SVGModel> |
||||
</y:SVGNode> |
||||
</data> |
||||
</node> |
||||
<node id="n4"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="34.0" y="197.5"/> |
||||
<y:Fill color="#99CCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="64.71484375" x="19.142578125" y="25.1494140625">updatePost<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n5"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="193.0" y="197.5"/> |
||||
<y:Fill color="#99CCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="89.388671875" x="6.8056640625" y="25.1494140625">updateOwnPost<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n6"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="352.0" y="197.5"/> |
||||
<y:Fill color="#99CCFF" transparent="false"/> |
||||
<y:BorderStyle color="#FF0000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" modelName="custom" textColor="#000000" visible="true" width="61.36328125" x="20.818359375" y="25.1494140625">createPost<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n7"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="29.535858154296875" width="103.0" x="193.0" y="167.96414184570312"/> |
||||
<y:Fill color="#FFCC00" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="64.03515625" x="19.482421875" y="5.4173431396484375">AuthorRule<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<edge id="e0" source="n4" target="n0"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e1" source="n4" target="n5"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e2" source="n1" target="n0"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#FF0000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e3" source="n6" target="n1"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"> |
||||
<y:Point x="403.5" y="23.0"/> |
||||
</y:Path> |
||||
<y:LineStyle color="#FF0000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e4" source="n1" target="n2"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e5" source="n0" target="n3"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#FF0000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e6" source="n7" target="n1"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
</graph> |
||||
<data key="d0"> |
||||
<y:Resources> |
||||
<y:Resource id="1"><?xml version="1.0" encoding="utf-8"?> |
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" |
||||
width="57px" height="65px" viewBox="0 0 57 65" enable-background="new 0 0 57 65" xml:space="preserve"> |
||||
<g> |
||||
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> |
||||
<stop offset="0.2711" style="stop-color:#FFAB4F"/> |
||||
<stop offset="1" style="stop-color:#FFD28F"/> |
||||
</linearGradient> |
||||
<path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 |
||||
V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 |
||||
c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> |
||||
<path id="body_18_" fill="#ECECEC" stroke="#9B9B9B" stroke-miterlimit="10" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 |
||||
c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146 |
||||
c-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021 |
||||
C1.378,56.689,0.5,62.768,0.5,62.768z"/> |
||||
|
||||
<radialGradient id="SVGID_2_" cx="22.6621" cy="21.707" r="17.7954" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_2_)" stroke="#E55E03" d="M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438 |
||||
c0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051 |
||||
c0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387 |
||||
C40.445,49.917,26.288,53.051,26.288,53.051z"/> |
||||
|
||||
<radialGradient id="SVGID_3_" cx="15.2056" cy="831.1875" r="32.3071" gradientTransform="matrix(1 0 0 1 0.0801 -773.6914)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_3_)" stroke="#E55E03" d="M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67 |
||||
c-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25 |
||||
c-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492 |
||||
C56.055,62.768,54.211,55.906,49.529,51.225z"/> |
||||
|
||||
<radialGradient id="SVGID_4_" cx="17.0723" cy="18.4907" r="11.8931" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_4_)" stroke="#E55E03" d="M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397 |
||||
c-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359 |
||||
c-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z"/> |
||||
|
||||
<radialGradient id="SVGID_5_" cx="31.8184" cy="19.3525" r="14.63" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_5_)" stroke="#E55E03" d="M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617 |
||||
c0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313 |
||||
c3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z"/> |
||||
|
||||
<radialGradient id="SVGID_6_" cx="30.4893" cy="4.8721" r="5.2028" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_6_)" stroke="#E55E03" d="M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813 |
||||
c-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75"/> |
||||
|
||||
<radialGradient id="SVGID_7_" cx="23.2871" cy="5.3008" r="5.5143" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_7_)" stroke="#E55E03" d="M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3 |
||||
c0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333"/> |
||||
|
||||
<radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FFD28F"/> |
||||
<stop offset="1" style="stop-color:#FFAB4F"/> |
||||
</radialGradient> |
||||
<path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 |
||||
c0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 |
||||
C36.627,4.945,43.59,13.158,43.676,23.357z"/> |
||||
|
||||
<linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.501" y1="-12291.5195" x2="6492.1304" y2="-12384.9688" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)"> |
||||
<stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> |
||||
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> |
||||
</linearGradient> |
||||
<path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 |
||||
c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 |
||||
c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> |
||||
<path id="Hair_Young_Black_1_" fill="#5C5C5C" stroke="#353535" stroke-linecap="round" stroke-linejoin="round" d="M20.278,13.25 |
||||
c3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807 |
||||
s3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678 |
||||
L14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z"/> |
||||
</g> |
||||
</svg> |
||||
</y:Resource> |
||||
<y:Resource id="2"><?xml version="1.0" encoding="utf-8"?> |
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" |
||||
width="57px" height="67px" viewBox="0 0 57 67" enable-background="new 0 0 57 67" xml:space="preserve"> |
||||
<g> |
||||
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> |
||||
<stop offset="0.2711" style="stop-color:#FFAB4F"/> |
||||
<stop offset="1" style="stop-color:#FFD28F"/> |
||||
</linearGradient> |
||||
<path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 |
||||
V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 |
||||
c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> |
||||
|
||||
<radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FFD28F"/> |
||||
<stop offset="1" style="stop-color:#FFAB4F"/> |
||||
</radialGradient> |
||||
<path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 |
||||
c0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 |
||||
C36.627,4.945,43.59,13.158,43.676,23.357z"/> |
||||
|
||||
<linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.5" y1="-12286.8594" x2="6492.1294" y2="-12380.3086" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)"> |
||||
<stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> |
||||
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> |
||||
</linearGradient> |
||||
<path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 |
||||
c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 |
||||
c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> |
||||
<path id="Hair_Female_1_Red_1_" fill="#FAE1AA" stroke="#E2B354" stroke-linecap="round" stroke-linejoin="round" d="M28.372,0.5 |
||||
C17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526 |
||||
c-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228 |
||||
s2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781 |
||||
c15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z"/> |
||||
|
||||
<linearGradient id="body_1_" gradientUnits="userSpaceOnUse" x1="95.9063" y1="-3134.2153" x2="31.5133" y2="-3134.2153" gradientTransform="matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)"> |
||||
<stop offset="0" style="stop-color:#49AD33"/> |
||||
<stop offset="1" style="stop-color:#C2DA92"/> |
||||
</linearGradient> |
||||
<path id="body_8_" fill="url(#body_1_)" stroke="#008D33" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 |
||||
c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146 |
||||
c-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674 |
||||
C1.378,56.689,0.5,62.768,0.5,62.768z"/> |
||||
</g> |
||||
</svg> |
||||
</y:Resource> |
||||
</y:Resources> |
||||
</data> |
||||
</graphml> |
After Width: | Height: | Size: 19 KiB |
@ -0,0 +1,368 @@
|
||||
<?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.12.2--> |
||||
<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"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="34.0" y="-11.5"/> |
||||
<y:Fill color="#ADF4A6" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="36.68359375" x="33.158203125" y="25.1494140625">admin<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n1"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="193.0" y="-11.5"/> |
||||
<y:Fill color="#ADF4A6" transparent="false"/> |
||||
<y:BorderStyle color="#FF0000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" modelName="custom" textColor="#000000" visible="true" width="38.025390625" x="32.4873046875" y="25.1494140625">author<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n2"> |
||||
<data key="d6"> |
||||
<y:SVGNode> |
||||
<y:Geometry height="64.53585815429688" width="56.560157775878906" x="216.21992111206055" y="-132.03585815429688"/> |
||||
<y:Fill color="#CCCCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="62.37109375" x="-2.905467987060547" y="-27.814727783203125">John, ID=2<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="-0.5" offsetX="0.0" offsetY="-9.113555908203125" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:SVGNodeProperties usingVisualBounds="true"/> |
||||
<y:SVGModel svgBoundsPolicy="0"> |
||||
<y:SVGContent refid="1"/> |
||||
</y:SVGModel> |
||||
</y:SVGNode> |
||||
</data> |
||||
</node> |
||||
<node id="n3"> |
||||
<data key="d6"> |
||||
<y:SVGNode> |
||||
<y:Geometry height="66.76200103759766" width="56.554100036621094" x="57.22294998168945" y="-133.14892959594727"/> |
||||
<y:Fill color="#CCCCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="62.37109375" x="-2.908496856689453" y="-27.701656341552734">Jane, ID=1<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="-0.5" offsetX="0.0" offsetY="-9.000484466552734" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:SVGNodeProperties usingVisualBounds="true"/> |
||||
<y:SVGModel svgBoundsPolicy="0"> |
||||
<y:SVGContent refid="2"/> |
||||
</y:SVGModel> |
||||
</y:SVGNode> |
||||
</data> |
||||
</node> |
||||
<node id="n4"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="34.0" y="197.5"/> |
||||
<y:Fill color="#99CCFF" transparent="false"/> |
||||
<y:BorderStyle color="#FF0000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" modelName="custom" textColor="#000000" visible="true" width="64.71484375" x="19.142578125" y="25.1494140625">updatePost<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n5"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="193.0" y="197.5"/> |
||||
<y:Fill color="#99CCFF" transparent="false"/> |
||||
<y:BorderStyle color="#FF0000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" modelName="custom" textColor="#000000" visible="true" width="89.388671875" x="6.8056640625" y="25.1494140625">updateOwnPost<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n6"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="352.0" y="197.5"/> |
||||
<y:Fill color="#99CCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="61.36328125" x="20.818359375" y="25.1494140625">createPost<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n7"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="29.535858154296875" width="103.0" x="193.0" y="167.96414184570312"/> |
||||
<y:Fill color="#FFCC00" transparent="false"/> |
||||
<y:BorderStyle color="#FF0000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" modelName="custom" textColor="#000000" visible="true" width="64.03515625" x="19.482421875" y="5.4173431396484375">AuthorRule<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<edge id="e0" source="n4" target="n0"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e1" source="n4" target="n5"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#FF0000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e2" source="n1" target="n0"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e3" source="n6" target="n1"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"> |
||||
<y:Point x="403.5" y="23.0"/> |
||||
</y:Path> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e4" source="n1" target="n2"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#FF0000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e5" source="n0" target="n3"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e6" source="n7" target="n1"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#FF0000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
</graph> |
||||
<data key="d0"> |
||||
<y:Resources> |
||||
<y:Resource id="1"><?xml version="1.0" encoding="utf-8"?> |
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" |
||||
width="57px" height="65px" viewBox="0 0 57 65" enable-background="new 0 0 57 65" xml:space="preserve"> |
||||
<g> |
||||
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> |
||||
<stop offset="0.2711" style="stop-color:#FFAB4F"/> |
||||
<stop offset="1" style="stop-color:#FFD28F"/> |
||||
</linearGradient> |
||||
<path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 |
||||
V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 |
||||
c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> |
||||
<path id="body_18_" fill="#ECECEC" stroke="#9B9B9B" stroke-miterlimit="10" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 |
||||
c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146 |
||||
c-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021 |
||||
C1.378,56.689,0.5,62.768,0.5,62.768z"/> |
||||
|
||||
<radialGradient id="SVGID_2_" cx="22.6621" cy="21.707" r="17.7954" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_2_)" stroke="#E55E03" d="M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438 |
||||
c0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051 |
||||
c0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387 |
||||
C40.445,49.917,26.288,53.051,26.288,53.051z"/> |
||||
|
||||
<radialGradient id="SVGID_3_" cx="15.2056" cy="831.1875" r="32.3071" gradientTransform="matrix(1 0 0 1 0.0801 -773.6914)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_3_)" stroke="#E55E03" d="M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67 |
||||
c-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25 |
||||
c-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492 |
||||
C56.055,62.768,54.211,55.906,49.529,51.225z"/> |
||||
|
||||
<radialGradient id="SVGID_4_" cx="17.0723" cy="18.4907" r="11.8931" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_4_)" stroke="#E55E03" d="M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397 |
||||
c-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359 |
||||
c-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z"/> |
||||
|
||||
<radialGradient id="SVGID_5_" cx="31.8184" cy="19.3525" r="14.63" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_5_)" stroke="#E55E03" d="M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617 |
||||
c0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313 |
||||
c3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z"/> |
||||
|
||||
<radialGradient id="SVGID_6_" cx="30.4893" cy="4.8721" r="5.2028" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_6_)" stroke="#E55E03" d="M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813 |
||||
c-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75"/> |
||||
|
||||
<radialGradient id="SVGID_7_" cx="23.2871" cy="5.3008" r="5.5143" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_7_)" stroke="#E55E03" d="M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3 |
||||
c0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333"/> |
||||
|
||||
<radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FFD28F"/> |
||||
<stop offset="1" style="stop-color:#FFAB4F"/> |
||||
</radialGradient> |
||||
<path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 |
||||
c0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 |
||||
C36.627,4.945,43.59,13.158,43.676,23.357z"/> |
||||
|
||||
<linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.501" y1="-12291.5195" x2="6492.1304" y2="-12384.9688" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)"> |
||||
<stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> |
||||
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> |
||||
</linearGradient> |
||||
<path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 |
||||
c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 |
||||
c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> |
||||
<path id="Hair_Young_Black_1_" fill="#5C5C5C" stroke="#353535" stroke-linecap="round" stroke-linejoin="round" d="M20.278,13.25 |
||||
c3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807 |
||||
s3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678 |
||||
L14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z"/> |
||||
</g> |
||||
</svg> |
||||
</y:Resource> |
||||
<y:Resource id="2"><?xml version="1.0" encoding="utf-8"?> |
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" |
||||
width="57px" height="67px" viewBox="0 0 57 67" enable-background="new 0 0 57 67" xml:space="preserve"> |
||||
<g> |
||||
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> |
||||
<stop offset="0.2711" style="stop-color:#FFAB4F"/> |
||||
<stop offset="1" style="stop-color:#FFD28F"/> |
||||
</linearGradient> |
||||
<path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 |
||||
V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 |
||||
c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> |
||||
|
||||
<radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FFD28F"/> |
||||
<stop offset="1" style="stop-color:#FFAB4F"/> |
||||
</radialGradient> |
||||
<path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 |
||||
c0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 |
||||
C36.627,4.945,43.59,13.158,43.676,23.357z"/> |
||||
|
||||
<linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.5" y1="-12286.8594" x2="6492.1294" y2="-12380.3086" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)"> |
||||
<stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> |
||||
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> |
||||
</linearGradient> |
||||
<path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 |
||||
c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 |
||||
c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> |
||||
<path id="Hair_Female_1_Red_1_" fill="#FAE1AA" stroke="#E2B354" stroke-linecap="round" stroke-linejoin="round" d="M28.372,0.5 |
||||
C17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526 |
||||
c-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228 |
||||
s2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781 |
||||
c15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z"/> |
||||
|
||||
<linearGradient id="body_1_" gradientUnits="userSpaceOnUse" x1="95.9063" y1="-3134.2153" x2="31.5133" y2="-3134.2153" gradientTransform="matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)"> |
||||
<stop offset="0" style="stop-color:#49AD33"/> |
||||
<stop offset="1" style="stop-color:#C2DA92"/> |
||||
</linearGradient> |
||||
<path id="body_8_" fill="url(#body_1_)" stroke="#008D33" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 |
||||
c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146 |
||||
c-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674 |
||||
C1.378,56.689,0.5,62.768,0.5,62.768z"/> |
||||
</g> |
||||
</svg> |
||||
</y:Resource> |
||||
</y:Resources> |
||||
</data> |
||||
</graphml> |
After Width: | Height: | Size: 19 KiB |
@ -0,0 +1,368 @@
|
||||
<?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.12.2--> |
||||
<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"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="34.0" y="-11.5"/> |
||||
<y:Fill color="#ADF4A6" transparent="false"/> |
||||
<y:BorderStyle color="#FF0000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" modelName="custom" textColor="#000000" visible="true" width="36.68359375" x="33.158203125" y="25.1494140625">admin<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n1"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="193.0" y="-11.5"/> |
||||
<y:Fill color="#ADF4A6" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="38.025390625" x="32.4873046875" y="25.1494140625">author<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n2"> |
||||
<data key="d6"> |
||||
<y:SVGNode> |
||||
<y:Geometry height="64.53585815429688" width="56.560157775878906" x="216.21992111206055" y="-132.03585815429688"/> |
||||
<y:Fill color="#CCCCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="62.37109375" x="-2.905467987060547" y="-27.814727783203125">John, ID=2<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="-0.5" offsetX="0.0" offsetY="-9.113555908203125" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:SVGNodeProperties usingVisualBounds="true"/> |
||||
<y:SVGModel svgBoundsPolicy="0"> |
||||
<y:SVGContent refid="1"/> |
||||
</y:SVGModel> |
||||
</y:SVGNode> |
||||
</data> |
||||
</node> |
||||
<node id="n3"> |
||||
<data key="d6"> |
||||
<y:SVGNode> |
||||
<y:Geometry height="66.76200103759766" width="56.554100036621094" x="57.22294998168945" y="-133.14892959594727"/> |
||||
<y:Fill color="#CCCCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="62.37109375" x="-2.908496856689453" y="-27.701656341552734">Jane, ID=1<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="-0.5" offsetX="0.0" offsetY="-9.000484466552734" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:SVGNodeProperties usingVisualBounds="true"/> |
||||
<y:SVGModel svgBoundsPolicy="0"> |
||||
<y:SVGContent refid="2"/> |
||||
</y:SVGModel> |
||||
</y:SVGNode> |
||||
</data> |
||||
</node> |
||||
<node id="n4"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="34.0" y="197.5"/> |
||||
<y:Fill color="#99CCFF" transparent="false"/> |
||||
<y:BorderStyle color="#FF0000" type="line" width="1.0"/> |
||||
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.701171875" modelName="custom" textColor="#000000" visible="true" width="64.71484375" x="19.142578125" y="25.1494140625">updatePost<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n5"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="193.0" y="197.5"/> |
||||
<y:Fill color="#99CCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="89.388671875" x="6.8056640625" y="25.1494140625">updateOwnPost<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n6"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="352.0" y="197.5"/> |
||||
<y:Fill color="#99CCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="61.36328125" x="20.818359375" y="25.1494140625">createPost<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n7"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="29.535858154296875" width="103.0" x="193.0" y="167.96414184570312"/> |
||||
<y:Fill color="#FFCC00" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="64.03515625" x="19.482421875" y="5.4173431396484375">AuthorRule<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<edge id="e0" source="n4" target="n0"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#FF0000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e1" source="n4" target="n5"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e2" source="n1" target="n0"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e3" source="n6" target="n1"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"> |
||||
<y:Point x="403.5" y="23.0"/> |
||||
</y:Path> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e4" source="n1" target="n2"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e5" source="n0" target="n3"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#FF0000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e6" source="n7" target="n1"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
</graph> |
||||
<data key="d0"> |
||||
<y:Resources> |
||||
<y:Resource id="1"><?xml version="1.0" encoding="utf-8"?> |
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" |
||||
width="57px" height="65px" viewBox="0 0 57 65" enable-background="new 0 0 57 65" xml:space="preserve"> |
||||
<g> |
||||
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> |
||||
<stop offset="0.2711" style="stop-color:#FFAB4F"/> |
||||
<stop offset="1" style="stop-color:#FFD28F"/> |
||||
</linearGradient> |
||||
<path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 |
||||
V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 |
||||
c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> |
||||
<path id="body_18_" fill="#ECECEC" stroke="#9B9B9B" stroke-miterlimit="10" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 |
||||
c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146 |
||||
c-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021 |
||||
C1.378,56.689,0.5,62.768,0.5,62.768z"/> |
||||
|
||||
<radialGradient id="SVGID_2_" cx="22.6621" cy="21.707" r="17.7954" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_2_)" stroke="#E55E03" d="M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438 |
||||
c0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051 |
||||
c0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387 |
||||
C40.445,49.917,26.288,53.051,26.288,53.051z"/> |
||||
|
||||
<radialGradient id="SVGID_3_" cx="15.2056" cy="831.1875" r="32.3071" gradientTransform="matrix(1 0 0 1 0.0801 -773.6914)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_3_)" stroke="#E55E03" d="M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67 |
||||
c-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25 |
||||
c-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492 |
||||
C56.055,62.768,54.211,55.906,49.529,51.225z"/> |
||||
|
||||
<radialGradient id="SVGID_4_" cx="17.0723" cy="18.4907" r="11.8931" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_4_)" stroke="#E55E03" d="M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397 |
||||
c-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359 |
||||
c-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z"/> |
||||
|
||||
<radialGradient id="SVGID_5_" cx="31.8184" cy="19.3525" r="14.63" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_5_)" stroke="#E55E03" d="M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617 |
||||
c0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313 |
||||
c3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z"/> |
||||
|
||||
<radialGradient id="SVGID_6_" cx="30.4893" cy="4.8721" r="5.2028" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_6_)" stroke="#E55E03" d="M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813 |
||||
c-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75"/> |
||||
|
||||
<radialGradient id="SVGID_7_" cx="23.2871" cy="5.3008" r="5.5143" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_7_)" stroke="#E55E03" d="M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3 |
||||
c0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333"/> |
||||
|
||||
<radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FFD28F"/> |
||||
<stop offset="1" style="stop-color:#FFAB4F"/> |
||||
</radialGradient> |
||||
<path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 |
||||
c0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 |
||||
C36.627,4.945,43.59,13.158,43.676,23.357z"/> |
||||
|
||||
<linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.501" y1="-12291.5195" x2="6492.1304" y2="-12384.9688" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)"> |
||||
<stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> |
||||
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> |
||||
</linearGradient> |
||||
<path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 |
||||
c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 |
||||
c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> |
||||
<path id="Hair_Young_Black_1_" fill="#5C5C5C" stroke="#353535" stroke-linecap="round" stroke-linejoin="round" d="M20.278,13.25 |
||||
c3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807 |
||||
s3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678 |
||||
L14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z"/> |
||||
</g> |
||||
</svg> |
||||
</y:Resource> |
||||
<y:Resource id="2"><?xml version="1.0" encoding="utf-8"?> |
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" |
||||
width="57px" height="67px" viewBox="0 0 57 67" enable-background="new 0 0 57 67" xml:space="preserve"> |
||||
<g> |
||||
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> |
||||
<stop offset="0.2711" style="stop-color:#FFAB4F"/> |
||||
<stop offset="1" style="stop-color:#FFD28F"/> |
||||
</linearGradient> |
||||
<path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 |
||||
V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 |
||||
c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> |
||||
|
||||
<radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FFD28F"/> |
||||
<stop offset="1" style="stop-color:#FFAB4F"/> |
||||
</radialGradient> |
||||
<path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 |
||||
c0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 |
||||
C36.627,4.945,43.59,13.158,43.676,23.357z"/> |
||||
|
||||
<linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.5" y1="-12286.8594" x2="6492.1294" y2="-12380.3086" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)"> |
||||
<stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> |
||||
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> |
||||
</linearGradient> |
||||
<path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 |
||||
c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 |
||||
c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> |
||||
<path id="Hair_Female_1_Red_1_" fill="#FAE1AA" stroke="#E2B354" stroke-linecap="round" stroke-linejoin="round" d="M28.372,0.5 |
||||
C17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526 |
||||
c-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228 |
||||
s2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781 |
||||
c15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z"/> |
||||
|
||||
<linearGradient id="body_1_" gradientUnits="userSpaceOnUse" x1="95.9063" y1="-3134.2153" x2="31.5133" y2="-3134.2153" gradientTransform="matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)"> |
||||
<stop offset="0" style="stop-color:#49AD33"/> |
||||
<stop offset="1" style="stop-color:#C2DA92"/> |
||||
</linearGradient> |
||||
<path id="body_8_" fill="url(#body_1_)" stroke="#008D33" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 |
||||
c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146 |
||||
c-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674 |
||||
C1.378,56.689,0.5,62.768,0.5,62.768z"/> |
||||
</g> |
||||
</svg> |
||||
</y:Resource> |
||||
</y:Resources> |
||||
</data> |
||||
</graphml> |
After Width: | Height: | Size: 19 KiB |
@ -0,0 +1,312 @@
|
||||
<?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.12.2--> |
||||
<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"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="34.0" y="-11.5"/> |
||||
<y:Fill color="#ADF4A6" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="36.68359375" x="33.158203125" y="25.1494140625">admin<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n1"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="193.0" y="-11.5"/> |
||||
<y:Fill color="#ADF4A6" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="38.025390625" x="32.4873046875" y="25.1494140625">author<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n2"> |
||||
<data key="d6"> |
||||
<y:SVGNode> |
||||
<y:Geometry height="64.53585815429688" width="56.560157775878906" x="216.21992111206055" y="-132.03585815429688"/> |
||||
<y:Fill color="#CCCCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="62.37109375" x="-2.905467987060547" y="-27.814727783203125">John, ID=2<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="-0.5" offsetX="0.0" offsetY="-9.113555908203125" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:SVGNodeProperties usingVisualBounds="true"/> |
||||
<y:SVGModel svgBoundsPolicy="0"> |
||||
<y:SVGContent refid="1"/> |
||||
</y:SVGModel> |
||||
</y:SVGNode> |
||||
</data> |
||||
</node> |
||||
<node id="n3"> |
||||
<data key="d6"> |
||||
<y:SVGNode> |
||||
<y:Geometry height="66.76200103759766" width="56.554100036621094" x="57.22294998168945" y="-133.14892959594727"/> |
||||
<y:Fill color="#CCCCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="62.37109375" x="-2.908496856689453" y="-27.701656341552734">Jane, ID=1<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="-0.5" offsetX="0.0" offsetY="-9.000484466552734" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:SVGNodeProperties usingVisualBounds="true"/> |
||||
<y:SVGModel svgBoundsPolicy="0"> |
||||
<y:SVGContent refid="2"/> |
||||
</y:SVGModel> |
||||
</y:SVGNode> |
||||
</data> |
||||
</node> |
||||
<node id="n4"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="34.0" y="197.5"/> |
||||
<y:Fill color="#99CCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="64.71484375" x="19.142578125" y="25.1494140625">updatePost<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n5"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="193.0" y="197.5"/> |
||||
<y:Fill color="#99CCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="61.36328125" x="20.818359375" y="25.1494140625">createPost<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<edge id="e0" source="n4" target="n0"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e1" source="n1" target="n0"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e2" source="n1" target="n2"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e3" source="n0" target="n3"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e4" source="n5" target="n1"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
</graph> |
||||
<data key="d0"> |
||||
<y:Resources> |
||||
<y:Resource id="1"><?xml version="1.0" encoding="utf-8"?> |
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" |
||||
width="57px" height="65px" viewBox="0 0 57 65" enable-background="new 0 0 57 65" xml:space="preserve"> |
||||
<g> |
||||
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> |
||||
<stop offset="0.2711" style="stop-color:#FFAB4F"/> |
||||
<stop offset="1" style="stop-color:#FFD28F"/> |
||||
</linearGradient> |
||||
<path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 |
||||
V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 |
||||
c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> |
||||
<path id="body_18_" fill="#ECECEC" stroke="#9B9B9B" stroke-miterlimit="10" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 |
||||
c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146 |
||||
c-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021 |
||||
C1.378,56.689,0.5,62.768,0.5,62.768z"/> |
||||
|
||||
<radialGradient id="SVGID_2_" cx="22.6621" cy="21.707" r="17.7954" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_2_)" stroke="#E55E03" d="M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438 |
||||
c0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051 |
||||
c0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387 |
||||
C40.445,49.917,26.288,53.051,26.288,53.051z"/> |
||||
|
||||
<radialGradient id="SVGID_3_" cx="15.2056" cy="831.1875" r="32.3071" gradientTransform="matrix(1 0 0 1 0.0801 -773.6914)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_3_)" stroke="#E55E03" d="M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67 |
||||
c-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25 |
||||
c-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492 |
||||
C56.055,62.768,54.211,55.906,49.529,51.225z"/> |
||||
|
||||
<radialGradient id="SVGID_4_" cx="17.0723" cy="18.4907" r="11.8931" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_4_)" stroke="#E55E03" d="M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397 |
||||
c-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359 |
||||
c-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z"/> |
||||
|
||||
<radialGradient id="SVGID_5_" cx="31.8184" cy="19.3525" r="14.63" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_5_)" stroke="#E55E03" d="M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617 |
||||
c0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313 |
||||
c3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z"/> |
||||
|
||||
<radialGradient id="SVGID_6_" cx="30.4893" cy="4.8721" r="5.2028" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_6_)" stroke="#E55E03" d="M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813 |
||||
c-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75"/> |
||||
|
||||
<radialGradient id="SVGID_7_" cx="23.2871" cy="5.3008" r="5.5143" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_7_)" stroke="#E55E03" d="M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3 |
||||
c0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333"/> |
||||
|
||||
<radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FFD28F"/> |
||||
<stop offset="1" style="stop-color:#FFAB4F"/> |
||||
</radialGradient> |
||||
<path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 |
||||
c0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 |
||||
C36.627,4.945,43.59,13.158,43.676,23.357z"/> |
||||
|
||||
<linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.501" y1="-12291.5195" x2="6492.1304" y2="-12384.9688" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)"> |
||||
<stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> |
||||
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> |
||||
</linearGradient> |
||||
<path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 |
||||
c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 |
||||
c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> |
||||
<path id="Hair_Young_Black_1_" fill="#5C5C5C" stroke="#353535" stroke-linecap="round" stroke-linejoin="round" d="M20.278,13.25 |
||||
c3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807 |
||||
s3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678 |
||||
L14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z"/> |
||||
</g> |
||||
</svg> |
||||
</y:Resource> |
||||
<y:Resource id="2"><?xml version="1.0" encoding="utf-8"?> |
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" |
||||
width="57px" height="67px" viewBox="0 0 57 67" enable-background="new 0 0 57 67" xml:space="preserve"> |
||||
<g> |
||||
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> |
||||
<stop offset="0.2711" style="stop-color:#FFAB4F"/> |
||||
<stop offset="1" style="stop-color:#FFD28F"/> |
||||
</linearGradient> |
||||
<path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 |
||||
V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 |
||||
c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> |
||||
|
||||
<radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FFD28F"/> |
||||
<stop offset="1" style="stop-color:#FFAB4F"/> |
||||
</radialGradient> |
||||
<path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 |
||||
c0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 |
||||
C36.627,4.945,43.59,13.158,43.676,23.357z"/> |
||||
|
||||
<linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.5" y1="-12286.8594" x2="6492.1294" y2="-12380.3086" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)"> |
||||
<stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> |
||||
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> |
||||
</linearGradient> |
||||
<path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 |
||||
c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 |
||||
c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> |
||||
<path id="Hair_Female_1_Red_1_" fill="#FAE1AA" stroke="#E2B354" stroke-linecap="round" stroke-linejoin="round" d="M28.372,0.5 |
||||
C17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526 |
||||
c-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228 |
||||
s2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781 |
||||
c15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z"/> |
||||
|
||||
<linearGradient id="body_1_" gradientUnits="userSpaceOnUse" x1="95.9063" y1="-3134.2153" x2="31.5133" y2="-3134.2153" gradientTransform="matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)"> |
||||
<stop offset="0" style="stop-color:#49AD33"/> |
||||
<stop offset="1" style="stop-color:#C2DA92"/> |
||||
</linearGradient> |
||||
<path id="body_8_" fill="url(#body_1_)" stroke="#008D33" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 |
||||
c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146 |
||||
c-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674 |
||||
C1.378,56.689,0.5,62.768,0.5,62.768z"/> |
||||
</g> |
||||
</svg> |
||||
</y:Resource> |
||||
</y:Resources> |
||||
</data> |
||||
</graphml> |
After Width: | Height: | Size: 16 KiB |
@ -0,0 +1,368 @@
|
||||
<?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.12.2--> |
||||
<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"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="34.0" y="-11.5"/> |
||||
<y:Fill color="#ADF4A6" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="36.68359375" x="33.158203125" y="25.1494140625">admin<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n1"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="193.0" y="-11.5"/> |
||||
<y:Fill color="#ADF4A6" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="38.025390625" x="32.4873046875" y="25.1494140625">author<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n2"> |
||||
<data key="d6"> |
||||
<y:SVGNode> |
||||
<y:Geometry height="64.53585815429688" width="56.560157775878906" x="216.21992111206055" y="-132.03585815429688"/> |
||||
<y:Fill color="#CCCCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="62.37109375" x="-2.905467987060547" y="-27.814727783203125">John, ID=2<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="-0.5" offsetX="0.0" offsetY="-9.113555908203125" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:SVGNodeProperties usingVisualBounds="true"/> |
||||
<y:SVGModel svgBoundsPolicy="0"> |
||||
<y:SVGContent refid="1"/> |
||||
</y:SVGModel> |
||||
</y:SVGNode> |
||||
</data> |
||||
</node> |
||||
<node id="n3"> |
||||
<data key="d6"> |
||||
<y:SVGNode> |
||||
<y:Geometry height="66.76200103759766" width="56.554100036621094" x="57.22294998168945" y="-133.14892959594727"/> |
||||
<y:Fill color="#CCCCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="62.37109375" x="-2.908496856689453" y="-27.701656341552734">Jane, ID=1<y:LabelModel> |
||||
<y:SmartNodeLabelModel distance="4.0"/> |
||||
</y:LabelModel> |
||||
<y:ModelParameter> |
||||
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.5" nodeRatioX="0.0" nodeRatioY="-0.5" offsetX="0.0" offsetY="-9.000484466552734" upX="0.0" upY="-1.0"/> |
||||
</y:ModelParameter> |
||||
</y:NodeLabel> |
||||
<y:SVGNodeProperties usingVisualBounds="true"/> |
||||
<y:SVGModel svgBoundsPolicy="0"> |
||||
<y:SVGContent refid="2"/> |
||||
</y:SVGModel> |
||||
</y:SVGNode> |
||||
</data> |
||||
</node> |
||||
<node id="n4"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="34.0" y="197.5"/> |
||||
<y:Fill color="#99CCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="64.71484375" x="19.142578125" y="25.1494140625">updatePost<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n5"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="193.0" y="197.5"/> |
||||
<y:Fill color="#99CCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="89.388671875" x="6.8056640625" y="25.1494140625">updateOwnPost<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n6"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="69.0" width="103.0" x="352.0" y="197.5"/> |
||||
<y:Fill color="#99CCFF" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="61.36328125" x="20.818359375" y="25.1494140625">createPost<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<node id="n7"> |
||||
<data key="d6"> |
||||
<y:ShapeNode> |
||||
<y:Geometry height="29.535858154296875" width="103.0" x="193.0" y="167.96414184570312"/> |
||||
<y:Fill color="#FFCC00" 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.701171875" modelName="custom" textColor="#000000" visible="true" width="64.03515625" x="19.482421875" y="5.4173431396484375">AuthorRule<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="roundrectangle"/> |
||||
</y:ShapeNode> |
||||
</data> |
||||
</node> |
||||
<edge id="e0" source="n4" target="n0"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e1" source="n4" target="n5"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e2" source="n1" target="n0"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e3" source="n6" target="n1"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"> |
||||
<y:Point x="403.5" y="23.0"/> |
||||
</y:Path> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e4" source="n1" target="n2"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e5" source="n0" target="n3"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
<edge id="e6" source="n7" target="n1"> |
||||
<data key="d10"> |
||||
<y:PolyLineEdge> |
||||
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
||||
<y:LineStyle color="#000000" type="line" width="1.0"/> |
||||
<y:Arrows source="none" target="standard"/> |
||||
<y:BendStyle smoothed="false"/> |
||||
</y:PolyLineEdge> |
||||
</data> |
||||
</edge> |
||||
</graph> |
||||
<data key="d0"> |
||||
<y:Resources> |
||||
<y:Resource id="1"><?xml version="1.0" encoding="utf-8"?> |
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" |
||||
width="57px" height="65px" viewBox="0 0 57 65" enable-background="new 0 0 57 65" xml:space="preserve"> |
||||
<g> |
||||
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> |
||||
<stop offset="0.2711" style="stop-color:#FFAB4F"/> |
||||
<stop offset="1" style="stop-color:#FFD28F"/> |
||||
</linearGradient> |
||||
<path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 |
||||
V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 |
||||
c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> |
||||
<path id="body_18_" fill="#ECECEC" stroke="#9B9B9B" stroke-miterlimit="10" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 |
||||
c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-11.244-6.146-11.244-6.146 |
||||
c-1.771,1.655-5.61,3.802-10.063,3.802c-4.453,0-8.292-2.146-10.063-3.802c0,0-5.755,0.586-11.189,6.021 |
||||
C1.378,56.689,0.5,62.768,0.5,62.768z"/> |
||||
|
||||
<radialGradient id="SVGID_2_" cx="22.6621" cy="21.707" r="17.7954" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_2_)" stroke="#E55E03" d="M28.106,33.486c-8.112,0-12.688,4.313-12.688,10.438 |
||||
c0,7.422,12.688,10.438,12.688,10.438s14.688-3.016,14.688-10.438C42.793,38.75,36.215,33.486,28.106,33.486z M26.288,53.051 |
||||
c0,0-7.135-2.093-8.805-7.201c-0.222-0.682,0.147-1.156,0.795-1.521V37.8h20.188v6.663c0.235,0.352,1.109,0.737,1.229,1.387 |
||||
C40.445,49.917,26.288,53.051,26.288,53.051z"/> |
||||
|
||||
<radialGradient id="SVGID_3_" cx="15.2056" cy="831.1875" r="32.3071" gradientTransform="matrix(1 0 0 1 0.0801 -773.6914)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_3_)" stroke="#E55E03" d="M49.529,51.225c-2.239-2.24-5.041-3.724-7.396-4.67 |
||||
c-2.854,5.51-14.021,7.807-14.021,7.807s-10.472-2.483-12.387-8.514c-2.439,0.771-5.787,2.287-8.749,5.25 |
||||
c-5.592,5.592-6.47,11.67-6.47,11.67c0,1.938,1.575,3.492,3.523,3.492h48.51c1.946,0,3.521-1.558,3.521-3.492 |
||||
C56.055,62.768,54.211,55.906,49.529,51.225z"/> |
||||
|
||||
<radialGradient id="SVGID_4_" cx="17.0723" cy="18.4907" r="11.8931" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_4_)" stroke="#E55E03" d="M13.404,44.173c1.15-1.81,2.039-3.832,3.332-5.397 |
||||
c-0.514,1.027-1.669,4.084-1.669,5.148c0,5.186,10.366,9.079,14.688,10.438c-3.472,1.627-9.134-1.498-11.334-2.359 |
||||
c-3.601-1.419-4.071-3.063-5.89-4.854C12.523,47.135,12.878,45,13.404,44.173z"/> |
||||
|
||||
<radialGradient id="SVGID_5_" cx="31.8184" cy="19.3525" r="14.63" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_5_)" stroke="#E55E03" d="M45.777,43.924c-1.317-1.568-5.11-9.424-6.604-6.617 |
||||
c0.516,1.025,3.617,3.693,3.617,6.617c0,5.186-10.271,8.576-16.699,9.145c1.429,4.938,11.373,1.293,13.805-0.313 |
||||
c3.563-2.354,4.563-5.133,7.854-3.705C47.754,49.045,48.006,46.574,45.777,43.924z"/> |
||||
|
||||
<radialGradient id="SVGID_6_" cx="30.4893" cy="4.8721" r="5.2028" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_6_)" stroke="#E55E03" d="M30.777,54.167c0.357,0.836-0.153,1.983-0.352,2.813 |
||||
c-0.256,1.084-0.072,2.104,0.102,3.186c0.164,1.02,0.156,2.107,0.25,3.167c0.082,0.916,0.482,1.849,0.357,2.75"/> |
||||
|
||||
<radialGradient id="SVGID_7_" cx="23.2871" cy="5.3008" r="5.5143" gradientTransform="matrix(1 0 0 -1 0.04 64.1543)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FCB57A"/> |
||||
<stop offset="1" style="stop-color:#FF8C36"/> |
||||
</radialGradient> |
||||
<path fill="url(#SVGID_7_)" stroke="#E55E03" d="M23.695,53.417c-0.508,0.584-0.476,2.209-0.398,3 |
||||
c0.116,1.183,0.456,2.099,0.333,3.333c-0.192,1.943,0.154,4.479-0.436,6.333"/> |
||||
|
||||
<radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FFD28F"/> |
||||
<stop offset="1" style="stop-color:#FFAB4F"/> |
||||
</radialGradient> |
||||
<path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 |
||||
c0.086,10.2-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 |
||||
C36.627,4.945,43.59,13.158,43.676,23.357z"/> |
||||
|
||||
<linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.501" y1="-12291.5195" x2="6492.1304" y2="-12384.9688" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3351.7349)"> |
||||
<stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> |
||||
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> |
||||
</linearGradient> |
||||
<path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 |
||||
c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 |
||||
c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> |
||||
<path id="Hair_Young_Black_1_" fill="#5C5C5C" stroke="#353535" stroke-linecap="round" stroke-linejoin="round" d="M20.278,13.25 |
||||
c3.417,4.333,9.333,6.917,9.333,6.917l-1.417-3.5c0,0,7.094,4.691,8.083,4.333c0.968-0.2-1.082-3.807-1.082-3.807 |
||||
s3.138,1.795,4.854,3.969c1.803,2.28,4.285,3.504,4.285,3.504S47.027,2.719,27.289,2.744C8.278,2.709,12.058,27.678,12.058,27.678 |
||||
L14.695,17c0,0,0.914,5.757,1.399,4.875C17.861,15.211,18.861,11.5,20.278,13.25z"/> |
||||
</g> |
||||
</svg> |
||||
</y:Resource> |
||||
<y:Resource id="2"><?xml version="1.0" encoding="utf-8"?> |
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" |
||||
width="57px" height="67px" viewBox="0 0 57 67" enable-background="new 0 0 57 67" xml:space="preserve"> |
||||
<g> |
||||
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="26.3398" y1="3115.7266" x2="27.5807" y2="3145.5239" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)"> |
||||
<stop offset="0.2711" style="stop-color:#FFAB4F"/> |
||||
<stop offset="1" style="stop-color:#FFD28F"/> |
||||
</linearGradient> |
||||
<path fill="url(#SVGID_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M49.529,51.225c-4.396-4.396-10.951-5.884-12.063-6.109 |
||||
V37.8H19.278c0,0,0.038,6.903,0,6.868c0,0-6.874,0.997-12.308,6.432C1.378,56.691,0.5,62.77,0.5,62.77 |
||||
c0,1.938,1.575,3.492,3.523,3.492h48.51c1.947,0,3.521-1.558,3.521-3.492C56.055,62.768,54.211,55.906,49.529,51.225z"/> |
||||
|
||||
<radialGradient id="face_x5F_white_1_" cx="27.5835" cy="3117.4922" r="23.425" fx="23.0139" fy="3115.0024" gradientTransform="matrix(1 0 0 1 0.3203 -3091.7656)" gradientUnits="userSpaceOnUse"> |
||||
<stop offset="0" style="stop-color:#FFD28F"/> |
||||
<stop offset="1" style="stop-color:#FFAB4F"/> |
||||
</radialGradient> |
||||
<path id="face_x5F_white_3_" fill="url(#face_x5F_white_1_)" stroke="#ED9135" stroke-miterlimit="10" d="M43.676,23.357 |
||||
c0.086,10.199-6.738,18.52-15.25,18.586c-8.5,0.068-15.464-8.146-15.55-18.344C12.794,13.4,19.618,5.079,28.123,5.012 |
||||
C36.627,4.945,43.59,13.158,43.676,23.357z"/> |
||||
|
||||
<linearGradient id="face_highlight_1_" gradientUnits="userSpaceOnUse" x1="6468.5" y1="-12286.8594" x2="6492.1294" y2="-12380.3086" gradientTransform="matrix(0.275 0 0 -0.2733 -1752.8849 -3350.4617)"> |
||||
<stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.24"/> |
||||
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.16"/> |
||||
</linearGradient> |
||||
<path id="face_highlight_3_" fill="url(#face_highlight_1_)" d="M28.415,5.625c-6.035,0.047-10.747,4.493-12.787,10.386 |
||||
c-0.664,1.919-0.294,4.043,0.98,5.629c2.73,3.398,5.729,6.283,9.461,8.088c3.137,1.518,7.535,2.385,11.893,1.247 |
||||
c2.274-0.592,3.988-2.459,4.375-4.766c0.187-1.094,0.293-2.289,0.283-3.553C42.54,13.244,36.729,5.56,28.415,5.625z"/> |
||||
<path id="Hair_Female_1_Red_1_" fill="#FAE1AA" stroke="#E2B354" stroke-linecap="round" stroke-linejoin="round" d="M28.372,0.5 |
||||
C17.537,0.5,8.269,7.748,9.153,26.125c0.563,6.563,5.862,12.042,9.366,13.531c-2.929-10.968-0.304-25.021-0.585-25.526 |
||||
c-0.281-0.505,3.536,6.728,3.536,6.728l3.183-8.312c5.541,4.28,0.393,11.309,1.049,11.058c4.26-1.631,5.34-9.228,5.34-9.228 |
||||
s2.729,3.657,2.701,5.504c-0.054,3.562,2.194-6.067,2.194-6.067l1.027,2.031c6.727,9.822,3.684,16.208,1.648,22.781 |
||||
c15.666-0.703,12.291-10.48,9.66-18.407C43.59,6.092,39.206,0.5,28.372,0.5z"/> |
||||
|
||||
<linearGradient id="body_1_" gradientUnits="userSpaceOnUse" x1="95.9063" y1="-3134.2153" x2="31.5133" y2="-3134.2153" gradientTransform="matrix(0.9852 0 0 -0.9852 -34.4844 -3031.9851)"> |
||||
<stop offset="0" style="stop-color:#49AD33"/> |
||||
<stop offset="1" style="stop-color:#C2DA92"/> |
||||
</linearGradient> |
||||
<path id="body_8_" fill="url(#body_1_)" stroke="#008D33" d="M0.5,62.768c0,1.938,1.575,3.494,3.523,3.494h48.51 |
||||
c1.947,0,3.521-1.559,3.521-3.494c0,0-1.844-6.861-6.525-11.543c-4.815-4.813-8.244-5.146-8.244-5.146 |
||||
c-1.444,6.983-8.555,8.786-13.007,8.786s-11.322-2.643-11.941-9.439c0,0-4.559,1.199-9.367,5.674 |
||||
C1.378,56.689,0.5,62.768,0.5,62.768z"/> |
||||
</g> |
||||
</svg> |
||||
</y:Resource> |
||||
</y:Resources> |
||||
</data> |
||||
</graphml> |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 138 KiB |
@ -0,0 +1,208 @@
|
||||
Subir Archivos |
||||
============== |
||||
|
||||
Subir archivos en Yii es normalmente realizado con la ayuda de [[yii\web\UploadedFile]], que encapsula cada archivo subido |
||||
en un objeto `UploadedFile`. Combinado con [[yii\widgets\ActiveForm]] y [modelos](structure-models.md), |
||||
puedes fácilmente implementar un mecanismo seguro de subida de archivos. |
||||
|
||||
|
||||
## Crear Modelos <span id="creating-models"></span> |
||||
|
||||
Al igual que al trabajar con entradas de texto plano, para subir un archivo debes crear una clase de modelo y utilizar un atributo |
||||
de dicho modelo para mantener la instancia del archivo subido. Debes también declarar una regla para validar la subida del archivo. |
||||
Por ejemplo, |
||||
|
||||
```php |
||||
namespace app\models; |
||||
|
||||
use yii\base\Model; |
||||
use yii\web\UploadedFile; |
||||
|
||||
class UploadForm extends Model |
||||
{ |
||||
/** |
||||
* @var UploadedFile |
||||
*/ |
||||
public $imageFile; |
||||
|
||||
public function rules() |
||||
{ |
||||
return [ |
||||
[['imageFile'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg'], |
||||
]; |
||||
} |
||||
|
||||
public function upload() |
||||
{ |
||||
if ($this->validate()) { |
||||
$this->imageFile->saveAs('uploads/' . $this->imageFile->baseName . '.' . $this->imageFile->extension); |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
``` |
||||
|
||||
En el código anterior, el atributo `imageFile` es utilizado para mantener una instancia del archivo subido. Este está asociado con |
||||
una regla de validación `file`, que utiliza [[yii\validators\FileValidator]] para asegurarse que el archivo a subir tenga extensión `png` o `jpg`. |
||||
El método `upload()` realizará la validación y guardará el archivo subido en el servidor. |
||||
|
||||
El validador `file` te permite chequear las extensiones, el tamaño, el tipo MIME, etc. Por favor consulta |
||||
la sección [Validadores del Framework](tutorial-core-validators.md#file) para más detalles. |
||||
|
||||
> Tip: Si estás subiendo una imagen, podrías considerar el utilizar el validador `image`. El validador `image` es |
||||
implementado a través de [[yii\validators\ImageValidator]], que verifica que un atributo haya recibido una imagen válida |
||||
que pueda ser tanto guardada como procesada utilizando la [Extensión Imagine](https://github.com/yiisoft/yii2-imagine). |
||||
|
||||
|
||||
## Renderizar Campos de Subida de Archivos <span id="rendering-file-input"></span> |
||||
|
||||
A continuación, crea un campo de subida de archivo en la vista: |
||||
|
||||
```php |
||||
<?php |
||||
use yii\widgets\ActiveForm; |
||||
?> |
||||
|
||||
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?> |
||||
|
||||
<?= $form->field($model, 'imageFile')->fileInput() ?> |
||||
|
||||
<button>Enviar</button> |
||||
|
||||
<?php ActiveForm::end() ?> |
||||
``` |
||||
|
||||
Es importante recordad que agregues la opción `enctype` al formulario para que el archivo pueda ser subido apropiadamente. |
||||
La llamada a `fileInput()` renderizará un tag `<input type="file">` que le permitirá al usuario seleccionar el archivo a subir. |
||||
|
||||
> Tip: desde la versión 2.0.8, [[yii\web\widgets\ActiveField::fileInput|fileInput]] agrega la opción `enctype` al formulario |
||||
automáticamente cuando se utiliza una campo de subida de archivo. |
||||
|
||||
## Uniendo Todo <span id="wiring-up"></span> |
||||
|
||||
Ahora, en una acción del controlador, escribe el código que una el modelo y la vista para implementar la subida de archivos: |
||||
|
||||
```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->imageFile = UploadedFile::getInstance($model, 'imageFile'); |
||||
if ($model->upload()) { |
||||
// el archivo se subió exitosamente |
||||
return; |
||||
} |
||||
} |
||||
|
||||
return $this->render('upload', ['model' => $model]); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
En el código anterior, cuando se envía el formulario, el método [[yii\web\UploadedFile::getInstance()]] es llamado |
||||
para representar el archivo subido como una instancia de `UploadedFile`. Entonces dependemos de la validación del modelo |
||||
para asegurarnos que el archivo subido es válido y entonces subirlo al servidor. |
||||
|
||||
|
||||
## Uploading Multiple Files <span id="uploading-multiple-files"></span> |
||||
|
||||
También puedes subir varios archivos a la vez, con algunos ajustes en el código de las subsecciones previas. |
||||
|
||||
Primero debes ajustar la clase del modelo, agregando la opción `maxFiles` en la regla de validación `file` para limitar |
||||
el número máximo de archivos a subir. Definir `maxFiles` como `0` significa que no hay límite en el número de archivos |
||||
a subir simultáneamente. El número máximo de archivos permitidos para subir simultáneamente está también limitado |
||||
por la directiva PHP [`max_file_uploads`](http://php.net/manual/en/ini.core.php#ini.max-file-uploads), |
||||
cuyo valor por defecto es 20. El método `upload()` debería también ser modificado para guardar los archivos uno a uno. |
||||
|
||||
```php |
||||
namespace app\models; |
||||
|
||||
use yii\base\Model; |
||||
use yii\web\UploadedFile; |
||||
|
||||
class UploadForm extends Model |
||||
{ |
||||
/** |
||||
* @var UploadedFile[] |
||||
*/ |
||||
public $imageFiles; |
||||
|
||||
public function rules() |
||||
{ |
||||
return [ |
||||
[['imageFiles'], 'file', 'skipOnEmpty' => false, 'extensions' => 'png, jpg', 'maxFiles' => 4], |
||||
]; |
||||
} |
||||
|
||||
public function upload() |
||||
{ |
||||
if ($this->validate()) { |
||||
foreach ($this->imageFiles as $file) { |
||||
$file->saveAs('uploads/' . $file->baseName . '.' . $file->extension); |
||||
} |
||||
return true; |
||||
} else { |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
``` |
||||
|
||||
En el archivo de la vista, debes agregar la opción `multiple` en la llamada a `fileInput()` de manera que el campo |
||||
pueda recibir varios archivos: |
||||
|
||||
```php |
||||
<?php |
||||
use yii\widgets\ActiveForm; |
||||
?> |
||||
|
||||
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]) ?> |
||||
|
||||
<?= $form->field($model, 'imageFiles[]')->fileInput(['multiple' => true, 'accept' => 'image/*']) ?> |
||||
|
||||
<button>Enviar</button> |
||||
|
||||
<?php ActiveForm::end() ?> |
||||
``` |
||||
|
||||
Y finalmente en la acción del controlador, debes llamar `UploadedFile::getInstances()` en vez de |
||||
`UploadedFile::getInstance()` para asignar un array de instancias `UploadedFile` a `UploadForm::imageFiles`. |
||||
|
||||
```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->imageFiles = UploadedFile::getInstances($model, 'imageFiles'); |
||||
if ($model->upload()) { |
||||
// el archivo fue subido exitosamente |
||||
return; |
||||
} |
||||
} |
||||
|
||||
return $this->render('upload', ['model' => $model]); |
||||
} |
||||
} |
||||
``` |
@ -0,0 +1,714 @@
|
||||
Validación de Entrada |
||||
===================== |
||||
|
||||
Como regla básica, nunca debes confiar en los datos recibidos de un usuario final y deberías validarlo siempre |
||||
antes de ponerlo en uso. |
||||
|
||||
Dado un [modelo](structure-models.md) poblado con entradas de usuarios, puedes validar esas entradas llamando al |
||||
método [[yii\base\Model::validate()]]. Dicho método devolverá un valor booleano indicando si la validación |
||||
tuvo éxito o no. En caso de que no, puedes obtener los mensajes de error de la propiedad [[yii\base\Model::errors]]. Por ejemplo, |
||||
|
||||
```php |
||||
$model = new \app\models\ContactForm(); |
||||
|
||||
// poblar los atributos del modelo desde la entrada del usuario |
||||
$model->load(\Yii::$app->request->post()); |
||||
// lo que es equivalente a: |
||||
// $model->attributes = \Yii::$app->request->post('ContactForm'); |
||||
|
||||
if ($model->validate()) { |
||||
// toda la entrada es válida |
||||
} else { |
||||
// la validación falló: $errors es un array que contienen los mensajes de error |
||||
$errors = $model->errors; |
||||
} |
||||
``` |
||||
|
||||
|
||||
## Declarar Reglas <span id="declaring-rules"></span> |
||||
|
||||
Para hacer que `validate()` realmente funcione, debes declarar reglas de validación para los atributos que planeas validar. |
||||
Esto debería hacerse sobrescribiendo el método [[yii\base\Model::rules()]]. El siguiente ejemplo muestra cómo |
||||
son declaradas las reglas de validación para el modelo `ContactForm`: |
||||
|
||||
```php |
||||
public function rules() |
||||
{ |
||||
return [ |
||||
// los atributos name, email, subject y body son obligatorios |
||||
[['name', 'email', 'subject', 'body'], 'required'], |
||||
|
||||
// el atributo email debe ser una dirección de email válida |
||||
['email', 'email'], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
El método [[yii\base\Model::rules()|rules()]] debe devolver un array de reglas, la cual cada una |
||||
tiene el siguiente formato: |
||||
|
||||
```php |
||||
[ |
||||
// requerido, especifica qué atributos deben ser validados por esta regla. |
||||
// Para un sólo atributo, puedes utilizar su nombre directamente |
||||
// sin tenerlo dentro de un array |
||||
['attribute1', 'attribute2', ...], |
||||
|
||||
// requerido, especifica de qué tipo es la regla. |
||||
// Puede ser un nombre de clase, un alias de validador, o el nombre de un método de validación |
||||
'validator', |
||||
|
||||
// opcional, especifica en qué escenario/s esta regla debe aplicarse |
||||
// si no se especifica, significa que la regla se aplica en todos los escenarios |
||||
// Puedes también configurar la opción "except" en caso de que quieras aplicar la regla |
||||
// en todos los escenarios salvo los listados |
||||
'on' => ['scenario1', 'scenario2', ...], |
||||
|
||||
// opcional, especifica atributos adicionales para el objeto validador |
||||
'property1' => 'value1', 'property2' => 'value2', ... |
||||
] |
||||
``` |
||||
|
||||
Por cada regla debes especificar al menos a cuáles atributos aplica la regla y cuál es el tipo de la regla. |
||||
Puedes especificar el tipo de regla de las siguientes maneras: |
||||
|
||||
* el alias de un validador propio del framework, tal como `required`, `in`, `date`, etc. Por favor consulta |
||||
[Validadores del núcleo](tutorial-core-validators.md) para la lista completa de todos los validadores incluidos. |
||||
* el nombre de un método de validación en la clase del modelo, o una función anónima. Consulta la |
||||
subsección [Validadores en Línea](#inline-validators) para más detalles. |
||||
* el nombre completo de una clase de validador. Por favor consulta la subsección [Validadores Independientes](#standalone-validators) |
||||
para más detalles. |
||||
|
||||
Una regla puede ser utilizada para validar uno o varios atributos, y un atributo puede ser validado por una o varias reglas. |
||||
Una regla puede ser aplicada en ciertos [escenarios](structure-models.md#scenarios) con tan sólo especificando la opción `on`. |
||||
Si no especificas una opción `on`, significa que la regla se aplicará en todos los escenarios. |
||||
|
||||
Cuando el método `validate()` es llamado, este sigue los siguientes pasos para realiza la validación: |
||||
|
||||
1. Determina cuáles atributos deberían ser validados obteniendo la lista de atributos de [[yii\base\Model::scenarios()]] |
||||
utilizando el [[yii\base\Model::scenario|scenario]] actual. Estos atributos son llamados *atributos activos*. |
||||
2. Determina cuáles reglas de validación deberían ser validados obteniendo la lista de reglas de [[yii\base\Model::rules()]] |
||||
utilizando el [[yii\base\Model::scenario|scenario]] actual. Estas reglas son llamadas *reglas activas*. |
||||
3. Utiliza cada regla activa para validar cada atributo activo que esté asociado a la regla. |
||||
Las reglas de validación son evaluadas en el orden en que están listadas. |
||||
|
||||
De acuerdo a los pasos de validación mostrados arriba, un atributo será validado si y sólo si |
||||
es un atributo activo declarado en `scenarios()` y está asociado a una o varias reglas activas |
||||
declaradas en `rules()`. |
||||
|
||||
> Note: Es práctico darle nombre a las reglas, por ej: |
||||
> ```php |
||||
> public function rules() |
||||
> { |
||||
> return [ |
||||
> // ... |
||||
> 'password' => [['password'], 'string', 'max' => 60], |
||||
> ]; |
||||
> } |
||||
> ``` |
||||
> |
||||
> Puedes utilizarlas en una subclase del modelo: |
||||
> |
||||
> ```php |
||||
> public function rules() |
||||
> { |
||||
> $rules = parent::rules(); |
||||
> unset($rules['password']); |
||||
> return $rules; |
||||
> } |
||||
|
||||
|
||||
### Personalizar Mensajes de Error <span id="customizing-error-messages"></span> |
||||
|
||||
La mayoría de los validadores tienen mensajes de error por defecto que serán agregados al modelo siendo validado cuando sus atributos |
||||
fallan la validación. Por ejemplo, el validador [[yii\validators\RequiredValidator|required]] agregará |
||||
el mensaje "Username no puede estar vacío." a un modelo cuando falla la validación del atributo `username` al utilizar esta regla. |
||||
|
||||
Puedes especificar el mensaje de error de una regla especificado la propiedad `message` al declarar la regla, |
||||
como a continuación, |
||||
|
||||
```php |
||||
public function rules() |
||||
{ |
||||
return [ |
||||
['username', 'required', 'message' => 'Por favor escoge un nombre de usuario.'], |
||||
]; |
||||
} |
||||
``` |
||||
|
||||
Algunos validadores pueden soportar mensajes de error adicionales para describir más precisamente las causas |
||||
del fallo de validación. Por ejemplo, el validador [[yii\validators\NumberValidator|number]] soporta |
||||
[[yii\validators\NumberValidator::tooBig|tooBig]] y [[yii\validators\NumberValidator::tooSmall|tooSmall]] |
||||
para describir si el fallo de validación es porque el valor siendo validado es demasiado grande o demasiado pequeño, respectivamente. |
||||
Puedes configurar estos mensajes de error tal como cualquier otroa propiedad del validador en una regla de validación. |
||||
|
||||
|
||||
### Eventos de Validación <span id="validation-events"></span> |
||||
|
||||
Cuando el método [[yii\base\Model::validate()]] es llamado, este llamará a dos métodos que puedes sobrescribir para personalizar |
||||
el proceso de validación: |
||||
|
||||
* [[yii\base\Model::beforeValidate()]]: la implementación por defecto lanzará un evento [[yii\base\Model::EVENT_BEFORE_VALIDATE]]. |
||||
Puedes tanto sobrescribir este método o responder a este evento para realizar algún trabajo de pre procesamiento |
||||
(por ej. normalizar datos de entrada) antes de que ocurra la validación en sí. El método debe devolver un booleano que indique |
||||
si la validación debe continuar o no. |
||||
* [[yii\base\Model::afterValidate()]]: la implementación por defecto lanzará un evento [[yii\base\Model::EVENT_AFTER_VALIDATE]]. |
||||
uedes tanto sobrescribir este método o responder a este evento para realizar algún trabajo de post procesamiento después |
||||
de completada la validación. |
||||
|
||||
|
||||
### Validación Condicional <span id="conditional-validation"></span> |
||||
|
||||
Para validar atributos sólo en determinadas condiciones, por ej. la validación de un atributo depende |
||||
del valor de otro atributo puedes utilizar la propiedad [[yii\validators\Validator::when|when]] |
||||
para definir la condición. Por ejemplo, |
||||
|
||||
```php |
||||
['state', 'required', 'when' => function($model) { |
||||
return $model->country == 'USA'; |
||||
}] |
||||
``` |
||||
|
||||
La propiedad [[yii\validators\Validator::when|when]] toma un método invocable PHP con la siguiente firma: |
||||
|
||||
```php |
||||
/** |
||||
* @param Model $model el modelo siendo validado |
||||
* @param string $attribute al atributo siendo validado |
||||
* @return boolean si la regla debe ser aplicada o no |
||||
*/ |
||||
function ($model, $attribute) |
||||
``` |
||||
|
||||
Si también necesitas soportar validación condicional del lado del cliente, debes configurar |
||||
la propiedad [[yii\validators\Validator::whenClient|whenClient]], que toma un string que representa una función JavaScript |
||||
cuyo valor de retorno determina si debe aplicarse la regla o no. Por ejemplo, |
||||
|
||||
```php |
||||
['state', 'required', 'when' => function ($model) { |
||||
return $model->country == 'USA'; |
||||
}, 'whenClient' => "function (attribute, value) { |
||||
return $('#country').val() == 'USA'; |
||||
}"] |
||||
``` |
||||
|
||||
|
||||
### Filtro de Datos <span id="data-filtering"></span> |
||||
|
||||
La entrada del usuario a menudo debe ser filtrada o pre procesada. Por ejemplo, podrías querer eliminar los espacions alrededor |
||||
de la entrada `username`. Puedes utilizar reglas de validación para lograrlo. |
||||
|
||||
Los siguientes ejemplos muestran cómo eliminar esos espacios en la entrada y cómo transformar entradas vacías en null utilizando |
||||
los validadores del framework [trim](tutorial-core-validators.md#trim) y [default](tutorial-core-validators.md#default): |
||||
|
||||
```php |
||||
return [ |
||||
[['username', 'email'], 'trim'], |
||||
[['username', 'email'], 'default'], |
||||
]; |
||||
``` |
||||
|
||||
También puedes utilizar el validador más general [filter](tutorial-core-validators.md#filter) para realizar filtros |
||||
de datos más complejos. |
||||
|
||||
Como puedes ver, estas reglas de validación no validan la entrada realmente. En cambio, procesan los valores |
||||
y los guardan en el atributo siendo validado. |
||||
|
||||
|
||||
### Manejando Entradas Vacías <span id="handling-empty-inputs"></span> |
||||
|
||||
Cuando los datos de entrada son enviados desde formularios HTML, a menudo necesitas asignar algunos valores por defecto a las entradas |
||||
si estas están vacías. Puedes hacerlo utilizando el validador [default](tutorial-core-validators.md#default). Por ejemplo, |
||||
|
||||
```php |
||||
return [ |
||||
// convierte "username" y "email" en null si estos están vacíos |
||||
[['username', 'email'], 'default'], |
||||
|
||||
// convierte "level" a 1 si está vacío |
||||
['level', 'default', 'value' => 1], |
||||
]; |
||||
``` |
||||
|
||||
Por defecto, una entrada se considera vacía si su valor es un string vacío, un array vacío o null. |
||||
Puedes personalizar la lógica de detección de valores vacíos configurando la propiedad [[yii\validators\Validator::isEmpty]] |
||||
con una función PHP invocable. Por ejemplo, |
||||
|
||||
```php |
||||
['agree', 'required', 'isEmpty' => function ($value) { |
||||
return empty($value); |
||||
}] |
||||
``` |
||||
|
||||
> Note: La mayoría de los validadores no manejan entradas vacías si su propiedad [[yii\validators\Validator::skipOnEmpty]] toma |
||||
el valor por defecto true. Estas serán simplemente salteadas durante la validación si sus atributos asociados reciben una entrada vacía. |
||||
Entre los [validadores del framework](tutorial-core-validators.md), sólo `captcha`, `default`, `filter`, |
||||
`required`, y `trim` manejarán entradas vacías. |
||||
|
||||
|
||||
## Validación Ad Hoc <span id="ad-hoc-validation"></span> |
||||
|
||||
A veces necesitas realizar *validación ad hoc* para valores que no están ligados a ningún modelo. |
||||
|
||||
Si sólo necesitas realizar un tipo de validación (por ej: validar direcciones de email), podrías llamar |
||||
al método [[yii\validators\Validator::validate()|validate()]] de los validadores deseados, como a continuación: |
||||
|
||||
```php |
||||
$email = 'test@example.com'; |
||||
$validator = new yii\validators\EmailValidator(); |
||||
|
||||
if ($validator->validate($email, $error)) { |
||||
echo 'Email válido.'; |
||||
} else { |
||||
echo $error; |
||||
} |
||||
``` |
||||
|
||||
> Note: No todos los validadores soportan este tipo de validación. Un ejemplo es el validador del framework [unique](tutorial-core-validators.md#unique), |
||||
que está diseñado para trabajar sólo con un modelo. |
||||
|
||||
Si necesitas realizar varias validaciones contro varios valores, puedes utilizar [[yii\base\DynamicModel]], |
||||
que soporta declarar tanto los atributos como las reglas sobre la marcha. Su uso es como a continuación: |
||||
|
||||
```php |
||||
public function actionSearch($name, $email) |
||||
{ |
||||
$model = DynamicModel::validateData(compact('name', 'email'), [ |
||||
[['name', 'email'], 'string', 'max' => 128], |
||||
['email', 'email'], |
||||
]); |
||||
|
||||
if ($model->hasErrors()) { |
||||
// validación fallida |
||||
} else { |
||||
// validación exitosa |
||||
} |
||||
} |
||||
``` |
||||
|
||||
El método [[yii\base\DynamicModel::validateData()]] crea una instancia de `DynamicModel`, define los atributos |
||||
utilizando los datos provistos (`name` e `email` en este ejemplo), y entonces llama a [[yii\base\Model::validate()]] |
||||
con las reglas provistas. |
||||
|
||||
Alternativamente, puedes utilizar la sintaxis más "clásica" para realizar la validación ad hoc: |
||||
|
||||
```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()) { |
||||
// validación fallida |
||||
} else { |
||||
// validación exitosa |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Después de la validación, puedes verificar si la validación tuvo éxito o no llamando al |
||||
método [[yii\base\DynamicModel::hasErrors()|hasErrors()]], obteniendo así los errores de validación de la |
||||
propiedad [[yii\base\DynamicModel::errors|errors]], como haces con un modelo normal. |
||||
Puedes también acceder a los atributos dinámicos definidos a través de la instancia del modelo, por ej., |
||||
`$model->name` y `$model->email`. |
||||
|
||||
|
||||
## Crear Validadores <span id="creating-validators"></span> |
||||
|
||||
Además de los [validadores del framework](tutorial-core-validators.md) incluidos en los lanzamientos de Yii, puedes también |
||||
crear tus propios validadores. Puedes crear validadores en línea o validadores independientes. |
||||
|
||||
|
||||
### Validadores en Línea <span id="inline-validators"></span> |
||||
|
||||
Un validador en línea es uno definido en términos del método de un modelo o una función anónima. La firma |
||||
del método/función es: |
||||
|
||||
```php |
||||
/** |
||||
* @param string $attribute el atributo siendo validado actualmente |
||||
* @param mixed $params el valor de los "parámetros" dados en la regla |
||||
*/ |
||||
function ($attribute, $params) |
||||
``` |
||||
|
||||
Si falla la validación de un atributo, el método/función debería llamar a [[yii\base\Model::addError()]] para guardar |
||||
el mensaje de error en el modelo de manera que pueda ser recuperado más tarde y presentado a los usuarios finales. |
||||
|
||||
Debajo hay algunos ejemplos: |
||||
|
||||
```php |
||||
use yii\base\Model; |
||||
|
||||
class MyForm extends Model |
||||
{ |
||||
public $country; |
||||
public $token; |
||||
|
||||
public function rules() |
||||
{ |
||||
return [ |
||||
// un validador en línea definido como el método del modelo validateCountry() |
||||
['country', 'validateCountry'], |
||||
|
||||
// un validador en línea definido como una función anónima |
||||
['token', function ($attribute, $params) { |
||||
if (!ctype_alnum($this->$attribute)) { |
||||
$this->addError($attribute, 'El token debe contener letras y dígitos.'); |
||||
} |
||||
}], |
||||
]; |
||||
} |
||||
|
||||
public function validateCountry($attribute, $params) |
||||
{ |
||||
if (!in_array($this->$attribute, ['USA', 'Web'])) { |
||||
$this->addError($attribute, 'El país debe ser "USA" o "Web".'); |
||||
} |
||||
} |
||||
} |
||||
``` |
||||
|
||||
> Note: Por defecto, los validadores en línea no serán aplicados si sus atributos asociados reciben entradas vacías |
||||
o si alguna de sus reglas de validación ya falló. Si quieres asegurarte de que una regla siempre sea aplicada, |
||||
puedes configurar las reglas [[yii\validators\Validator::skipOnEmpty|skipOnEmpty]] y/o [[yii\validators\Validator::skipOnError|skipOnError]] |
||||
como false en las declaraciones de las reglas. Por ejemplo: |
||||
> |
||||
> ```php |
||||
> [ |
||||
> ['country', 'validateCountry', 'skipOnEmpty' => false, 'skipOnError' => false], |
||||
> ] |
||||
> ``` |
||||
|
||||
|
||||
### Validadores Independientes <span id="standalone-validators"></span> |
||||
|
||||
Un validador independiente es una clase que extiende de [[yii\validators\Validator]] o sus sub clases. Puedes implementar |
||||
su lógica de validación sobrescribiendo el método [[yii\validators\Validator::validateAttribute()]]. Si falla la validación |
||||
de un atributo, llama a [[yii\base\Model::addError()]] para guardar el mensaje de error en el modelo, tal como haces |
||||
con los [validadores en línea](#inline-validators). |
||||
|
||||
|
||||
Por ejemplo, el validador en línea de arriba podría ser movida a una nueva clase [[components/validators/CountryValidator]]. |
||||
|
||||
```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, 'El país debe ser "USA" o "Web".'); |
||||
} |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Si quieres que tu validador soporte la validación de un valor sin modelo, deberías también sobrescribir |
||||
el método[[yii\validators\Validator::validate()]]. Puedes también sobrescribir [[yii\validators\Validator::validateValue()]] |
||||
en vez de `validateAttribute()` y `validate()` porque por defecto los últimos dos métodos son implementados |
||||
llamando a `validateValue()`. |
||||
|
||||
Debajo hay un ejemplo de cómo podrías utilizar la clase del validador de arriba dentro de tu modelo. |
||||
|
||||
```php |
||||
namespace app\models; |
||||
|
||||
use Yii; |
||||
use yii\base\Model; |
||||
use app\components\validators\CountryValidator; |
||||
|
||||
class EntryForm extends Model |
||||
{ |
||||
public $name; |
||||
public $email; |
||||
public $country; |
||||
|
||||
public function rules() |
||||
{ |
||||
return [ |
||||
[['name', 'email'], 'required'], |
||||
['country', CountryValidator::className()], |
||||
['email', 'email'], |
||||
]; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
|
||||
## Validación del Lado del Cliente <span id="client-side-validation"></span> |
||||
|
||||
La validación del lado del cliente basada en JavaScript es deseable cuando la entrada del usuario proviene de formularios HTML, dado que |
||||
permite a los usuarios encontrar errores más rápido y por lo tanto provee una mejor experiencia. Puedes utilizar o implementar |
||||
un validador que soporte validación del lado del cliente *en adición a* validación del lado del servidor. |
||||
|
||||
> Info: Si bien la validación del lado del cliente es deseable, no es una necesidad. Su principal propósito es proveer al usuario una mejor |
||||
experiencia. Al igual que datos de entrada que vienen del los usuarios finales, nunca deberías confiar en la validación del lado del cliente. Por esta razón, |
||||
deberías realizar siempre la validación del lado del servidor llamando a [[yii\base\Model::validate()]], como |
||||
se describió en las subsecciones previas. |
||||
|
||||
|
||||
### Utilizar Validación del Lado del Cliente <span id="using-client-side-validation"></span> |
||||
|
||||
Varios [validadores del framework](tutorial-core-validators.md) incluyen validación del lado del cliente. Todo lo que necesitas hacer |
||||
es solamente utilizar [[yii\widgets\ActiveForm]] para construir tus formularios HTML. Por ejemplo, `LoginForm` mostrado abajo declara dos |
||||
reglas: una utiliza el validador del framework [required](tutorial-core-validators.md#required), el cual es soportado tanto en |
||||
lado del cliente como del servidor; y el otro usa el validador en línea `validatePassword`, que es sólo soportado de lado |
||||
del servidor. |
||||
|
||||
```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 y password son ambos requeridos |
||||
[['username', 'password'], 'required'], |
||||
|
||||
// password es validado por validatePassword() |
||||
['password', 'validatePassword'], |
||||
]; |
||||
} |
||||
|
||||
public function validatePassword() |
||||
{ |
||||
$user = User::findByUsername($this->username); |
||||
|
||||
if (!$user || !$user->validatePassword($this->password)) { |
||||
$this->addError('password', 'Username o password incorrecto.'); |
||||
} |
||||
} |
||||
} |
||||
``` |
||||
|
||||
El formulario HTML creado en el siguiente código contiene dos campos de entrada: `username` y `password`. |
||||
Si envias el formulario sin escribir nada, encontrarás que los mensajes de error requiriendo que |
||||
escribas algo aparecen sin que haya comunicación alguna con el servidor. |
||||
|
||||
```php |
||||
<?php $form = yii\widgets\ActiveForm::begin(); ?> |
||||
<?= $form->field($model, 'username') ?> |
||||
<?= $form->field($model, 'password')->passwordInput() ?> |
||||
<?= Html::submitButton('Login') ?> |
||||
<?php yii\widgets\ActiveForm::end(); ?> |
||||
``` |
||||
|
||||
Detrás de escena, [[yii\widgets\ActiveForm]] leerá las reglas de validación declaradas en el modelo |
||||
y generará el código JavaScript apropiado para los validadores que soportan validación del lado del cliente. Cuando un usuario |
||||
cambia el valor de un campo o envia el formulario, se lanzará la validación JavaScript del lado del cliente. |
||||
|
||||
Si quieres deshabilitar la validación del lado del cliente completamente, puedes configurar |
||||
la propiedad [[yii\widgets\ActiveForm::enableClientValidation]] como false. También puedes deshabilitar la validación |
||||
del lado del cliente de campos individuales configurando su propiedad [[yii\widgets\ActiveField::enableClientValidation]] |
||||
como false. Cuando `enableClientValidation` es configurado tanto a nivel de campo como a nivel de formulario, |
||||
tendrá prioridad la primera. |
||||
|
||||
### Implementar Validación del Lado del Cliente <span id="implementing-client-side-validation"></span> |
||||
|
||||
|
||||
Para crear validadores que soportan validación del lado del cliente, debes implementar |
||||
el método [[yii\validators\Validator::clientValidateAttribute()]], que devuelve una pieza de código JavaScript |
||||
que realiza dicha validación. Dentro del código JavaScript, puedes utilizar las siguientes |
||||
variables predefinidas: |
||||
|
||||
- `attribute`: el nombre del atributo siendo validado. |
||||
- `value`: el valor siendo validado. |
||||
- `messages`: un array utilizado para contener los mensajes de error de validación para el atributo. |
||||
- `deferred`: un array con objetos diferidos puede ser insertado (explicado en la subsección siguiente). |
||||
|
||||
En el siguiente ejemplo, creamos un `StatusValidator` que valida si la entrada es un status válido |
||||
contra datos de status existentes. El validador soporta tato tanto validación del lado del servidor como del lado del cliente. |
||||
|
||||
```php |
||||
namespace app\components; |
||||
|
||||
use yii\validators\Validator; |
||||
use app\models\Status; |
||||
|
||||
class StatusValidator extends Validator |
||||
{ |
||||
public function init() |
||||
{ |
||||
parent::init(); |
||||
$this->message = 'Entrada de Status Inválida.'; |
||||
} |
||||
|
||||
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) === -1) { |
||||
messages.push($message); |
||||
} |
||||
JS; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
> Tip: El código de arriba muestra principalmente cómo soportar validación del lado del cliente. En la práctica, |
||||
> puedes utilizar el validador del framework [in](tutorial-core-validators.md#in) para alcanzar el mismo objetivo. Puedes |
||||
> escribir la regla de validación como a como a continuación: |
||||
> |
||||
> ```php |
||||
> [ |
||||
> ['status', 'in', 'range' => Status::find()->select('id')->asArray()->column()], |
||||
> ] |
||||
> ``` |
||||
|
||||
> Tip: Si necesitas trabajar con validación del lado del cliente manualmente, por ejemplo, agregar campos dinámicamente o realizar alguna lógica de UI, |
||||
> consulta [Trabajar con ActiveForm vía JavaScript](https://github.com/samdark/yii2-cookbook/blob/master/book/forms-activeform-js.md) |
||||
> en el Yii 2.0 Cookbook. |
||||
|
||||
### Validación Diferida <span id="deferred-validation"></span> |
||||
|
||||
Si necesitas realizar validación del lado del cliente asincrónica, puedes crear [Objetos Diferidos](http://api.jquery.com/category/deferred-object/). |
||||
Por ejemplo, para realizar validación AJAX personalizada, puedes utilizar el siguiente código: |
||||
|
||||
```php |
||||
public function clientValidateAttribute($model, $attribute, $view) |
||||
{ |
||||
return <<<JS |
||||
deferred.push($.get("/check", {value: value}).done(function(data) { |
||||
if ('' !== data) { |
||||
messages.push(data); |
||||
} |
||||
})); |
||||
JS; |
||||
} |
||||
``` |
||||
|
||||
Arriba, la variable `deferred` es provista por Yii, y es un array de Objetos Diferidos. El método `$.get()` |
||||
de jQuery crea un Objeto Diferido, el cual es insertado en el array `deferred`. |
||||
|
||||
Puedes también crear un Objeto Diferito explícitamente y llamar a su método `resolve()` cuando la llamada asincrónica |
||||
tiene lugar. El siguiente ejemplo muestra cómo validar las dimensiones de un archivo de imagen del lado del cliente. |
||||
|
||||
```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('Imagen demasiado ancha!!'); |
||||
} |
||||
def.resolve(); |
||||
} |
||||
var reader = new FileReader(); |
||||
reader.onloadend = function() { |
||||
img.src = reader.result; |
||||
} |
||||
reader.readAsDataURL(file); |
||||
|
||||
deferred.push(def); |
||||
JS; |
||||
} |
||||
``` |
||||
|
||||
> Note: El método `resolve()` debe ser llamado después de que el atributo ha sido validado. De otra manera la validación |
||||
principal del formulario no será completada. |
||||
|
||||
Por simplicidad, el array `deferred` está equipado con un método de atajo, `add()`, que automáticamente crea un |
||||
Objeto Diferido y lo agrega al array `deferred`. Utilizando este método, puedes simplificar el ejemplo de arriba de esta manera, |
||||
|
||||
```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('Imagen demasiado ancha!!'); |
||||
} |
||||
def.resolve(); |
||||
} |
||||
var reader = new FileReader(); |
||||
reader.onloadend = function() { |
||||
img.src = reader.result; |
||||
} |
||||
reader.readAsDataURL(file); |
||||
}); |
||||
JS; |
||||
} |
||||
``` |
||||
|
||||
|
||||
### Validación AJAX <span id="ajax-validation"></span> |
||||
|
||||
Algunas validaciones sólo pueden realizarse del lado del servidor, debido a que sólo el servidor tiene la información necesaria. |
||||
Por ejemplo, para validar si un nombre de usuario es único o no, es necesario revisar la tabla de usuarios del lado del servidor. |
||||
Puedes utilizar validación basada en AJAX en este caso. Esta lanzará una petición AJAX de fondo para validar |
||||
la entrada mientras se mantiene la misma experiencia de usuario como en una validación del lado del cliente regular. |
||||
|
||||
Para habilitar la validación AJAX individualmente un campo de entrada, configura la propiedad [[yii\widgets\ActiveField::enableAjaxValidation|enableAjaxValidation]] |
||||
de ese campo como true y especifica un único `id` de formulario: |
||||
|
||||
```php |
||||
use yii\widgets\ActiveForm; |
||||
|
||||
$form = ActiveForm::begin([ |
||||
'id' => 'registration-form', |
||||
]); |
||||
|
||||
echo $form->field($model, 'username', ['enableAjaxValidation' => true]); |
||||
|
||||
// ... |
||||
|
||||
ActiveForm::end(); |
||||
``` |
||||
|
||||
Para habiliar la validación AJAX en el formulario entero, configura [[yii\widgets\ActiveForm::enableAjaxValidation|enableAjaxValidation]] |
||||
como true a nivel del formulario: |
||||
|
||||
```php |
||||
$form = ActiveForm::begin([ |
||||
'id' => 'contact-form', |
||||
'enableAjaxValidation' => true, |
||||
]); |
||||
``` |
||||
|
||||
> Note: Cuando la propiedad `enableAjaxValidation` es configurada tanto a nivel de campo como a nivel de formulario, |
||||
la primera tendrá prioridad. |
||||
|
||||
Necesitas también preparar el servidor para que pueda manejar las peticiones AJAX. |
||||
Esto puede alcanzarse con una porción de código como la siguiente en las acciones del controlador: |
||||
|
||||
```php |
||||
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) { |
||||
Yii::$app->response->format = Response::FORMAT_JSON; |
||||
return ActiveForm::validate($model); |
||||
} |
||||
``` |
||||
|
||||
El código de arriba chequeará si la petición actual es AJAX o no. Si lo es, responderá |
||||
esta petición ejecutando la validación y devolviendo los errores en formato JSON. |
||||
|
||||
> Info: Puedes también utilizar [Validación Diferida](#deferred-validation) para realizar validación AJAX. |
||||
De todos modos, la característica de validación AJAX descrita aquí es más sistemática y requiere menos esfuerzo de escritura de código. |
||||
|
||||
Cuando tanto `enableClientValidation` como `enableAjaxValidation` son definidas como true, la petición de validación AJAX será lanzada |
||||
sólo después de una validación del lado del cliente exitosa. |
@ -0,0 +1,98 @@
|
||||
Trabajar con Scripts del Cliente |
||||
================================ |
||||
|
||||
> Note: Esta sección se encuentra en desarrollo. |
||||
|
||||
### Registrar scripts |
||||
|
||||
Con el objeto [[yii\web\View]] puedes registrar scripts. Hay dos métodos dedicados a esto: |
||||
[[yii\web\View::registerJs()|registerJs()]] para scripts en línea |
||||
[[yii\web\View::registerJsFile()|registerJsFile()]] para scripts externos. |
||||
Los scripts en línea son útiles para configuración y código generado dinámicamente. |
||||
El método para agregarlos puede ser utilizado así: |
||||
|
||||
```php |
||||
$this->registerJs("var options = ".json_encode($options).";", View::POS_END, 'my-options'); |
||||
``` |
||||
|
||||
El primer argumento es el código JS real que queremos insertar en la página. El segundo argumento |
||||
determina en qué parte de la página debería ser insertado el script. Los valores posibles son: |
||||
|
||||
- [[yii\web\View::POS_HEAD|View::POS_HEAD]] para la sección head. |
||||
- [[yii\web\View::POS_BEGIN|View::POS_BEGIN]] justo después de la etiqueta `<body>`. |
||||
- [[yii\web\View::POS_END|View::POS_END]] justo antes de cerrar la etiqueta `</body>`. |
||||
- [[yii\web\View::POS_READY|View::POS_READY]] para ejecutar código en el evento `ready` del documento. Esto registrará [[yii\web\JqueryAsset|jQuery]] automáticamente. |
||||
- [[yii\web\View::POS_LOAD|View::POS_LOAD]] para ejecutar código en el evento `load` del documento. Esto registrará [[yii\web\JqueryAsset|jQuery]] automáticamente. |
||||
|
||||
El último argumento es un ID único del script, utilizado para identificar el bloque de código y reemplazar otro con el mismo ID |
||||
en vez de agregar uno nuevo. En caso de no proveerlo, el código JS en sí será utilizado como ID. |
||||
|
||||
Un script externo puede ser agregado de esta manera: |
||||
|
||||
```php |
||||
$this->registerJsFile('http://example.com/js/main.js', ['depends' => [\yii\web\JqueryAsset::className()]]); |
||||
``` |
||||
|
||||
Los argumentos para [[yii\web\View::registerJsFile()|registerJsFile()]] son similares a los de |
||||
[[yii\web\View::registerCssFile()|registerCssFile()]]. En el ejemplo anterior, |
||||
registramos el archivo `main.js` con dependencia de `JqueryAsset`. Esto quiere decir que el archivo `main.js` |
||||
será agregado DESPUÉS de `jquery.js`. Si esta especificación de dependencia, el orden relativo entre |
||||
`main.js` y `jquery.js` sería indefinido. |
||||
|
||||
Como para [[yii\web\View::registerCssFile()|registerCssFile()]], es altamente recomendable que utilices |
||||
[asset bundles](structure-assets.md) para registrar archivos JS externos más que utilizar [[yii\web\View::registerJsFile()|registerJsFile()]]. |
||||
|
||||
|
||||
### Registrar asset bundles |
||||
|
||||
Como mencionamos anteriormente, es preferible utilizar asset bundles en vez de usar CSS y JavaScript directamente. Puedes obtener detalles |
||||
de cómo definir asset bundles en la sección [gestor de assets](structure-assets.md) de esta guía. Utilizar asset bundles |
||||
ya definidos es muy sencillo: |
||||
|
||||
```php |
||||
\frontend\assets\AppAsset::register($this); |
||||
``` |
||||
|
||||
|
||||
|
||||
### Registrar CSS |
||||
|
||||
Puedes registrar CSS utilizando [[yii\web\View::registerCss()|registerCss()]] o [[yii\web\View::registerCssFile()|registerCssFile()]]. |
||||
El primero registra un bloque de código CSS mientras que el segundo registra un archivo CSS externo. Por ejemplo, |
||||
|
||||
```php |
||||
$this->registerCss("body { background: #f00; }"); |
||||
``` |
||||
|
||||
El código anterior dará como resultado que se agregue lo siguiente a la sección head de la página: |
||||
|
||||
```html |
||||
<style> |
||||
body { background: #f00; } |
||||
</style> |
||||
``` |
||||
|
||||
Si quieres especificar propiedades adicionales a la etiqueta style, pasa un array de claves-valores como tercer argumento. |
||||
Si necesitas asegurarte que haya sólo una etiqueta style utiliza el cuarto argumento como fue mencionado en las descripciones de meta etiquetas. |
||||
|
||||
```php |
||||
$this->registerCssFile("http://example.com/css/themes/black-and-white.css", [ |
||||
'depends' => [BootstrapAsset::className()], |
||||
'media' => 'print', |
||||
], 'css-print-theme'); |
||||
``` |
||||
|
||||
El código de arriba agregará un link al archivo CSS en la sección head de la página. |
||||
|
||||
* El primer argumento especifica el archivo CSS a ser registrado. |
||||
* El segundo argumento especifica los atributos HTML de la etiqueta `<link>` resultante. La opción `depends` |
||||
es especialmente tratada. Esta especifica de qué asset bundles depende este archivo CSS. En este caso, depende |
||||
del asset bundle [[yii\bootstrap\BootstrapAsset|BootstrapAsset]]. Esto significa que el archivo CSS será agregado |
||||
*después* de los archivos CSS de [[yii\bootstrap\BootstrapAsset|BootstrapAsset]]. |
||||
* El último argumento especifica un ID que identifica al archivo CSS. Si no es provisto, se utilizará la URL |
||||
del archivo. |
||||
|
||||
|
||||
Es altamente recomendable que ustilices [asset bundles](structure-assets.md) para registrar archivos CSS en vez de |
||||
utilizar [[yii\web\View::registerCssFile()|registerCssFile()]]. Utilizar asset bundles te permite combinar y comprimir |
||||
varios archivos CSS, deseable en sitios web de tráfico alto. |
@ -0,0 +1,72 @@
|
||||
Paginación |
||||
========== |
||||
|
||||
Cuando hay muchos datos a mostrar en una sola página, una estrategia común es mostrarlos en varias |
||||
páginas y en cada una de ellas mostrar sólo una pequeña porción de datos. Esta estrategia es conocida como *paginación*. |
||||
|
||||
Yii utiliza el objeto [[yii\data\Pagination]] para representar la información acerca del esquema de paginación. En particular, |
||||
|
||||
* [[yii\data\Pagination::$totalCount|cuenta total]] especifica el número total de ítems de datos. Ten en cuenta que |
||||
este es normalmente un número mucho mayor que el número de ítems necesarios a mostrar en una simple página. |
||||
* [[yii\data\Pagination::$pageSize|tamaño de página]] especifica cuántos ítems de datos contiene cada página. El valor |
||||
por defecto es 20. |
||||
* [[yii\data\Pagination::$page|página actual]] da el número de la página actual (comenzando desde 0). El valor |
||||
por defecto es 0, lo que sería la primera página. |
||||
|
||||
Con un objeto [[yii\data\Pagination]] totalmente especificado, puedes obtener y mostrar datos en partes. Por ejemplo, |
||||
si estás recuperando datos de una base de datos, puedes especificar las cláusulas `OFFSET` y `LIMIT` de la consulta a la BD |
||||
correspondientes a los valores provistos por la paginación. A continuación hay un ejemplo, |
||||
|
||||
```php |
||||
use yii\data\Pagination; |
||||
|
||||
// construye una consulta a la BD para obtener todos los artículos con status = 1 |
||||
$query = Article::find()->where(['status' => 1]); |
||||
|
||||
// obtiene el número total de artículos (pero no recupera los datos de los artículos todavía) |
||||
$count = $query->count(); |
||||
|
||||
// crea un objeto paginación con dicho total |
||||
$pagination = new Pagination(['totalCount' => $count]); |
||||
|
||||
// limita la consulta utilizando la paginación y recupera los artículos |
||||
$articles = $query->offset($pagination->offset) |
||||
->limit($pagination->limit) |
||||
->all(); |
||||
``` |
||||
|
||||
¿Qué página de artículos devolverá el ejemplo de arriba? Depende de si se le es pasado un parámetro llamado `page`. |
||||
Por defecto, la paginación intentará definir la [[yii\data\Pagination::$page|página actual]] con |
||||
el valor del parámetro `page`. Si el parámetro no es provisto, entonces tomará por defecto el valor 0. |
||||
|
||||
Para facilitar la construcción de elementos UI que soporten paginación, Yii provee el widget [[yii\widgets\LinkPager]], |
||||
que muestra una lista de botones de navegación que el usuario puede presionar para indicar qué página de datos debería mostrarse. |
||||
El widget toma un objeto de paginación y tal manera conoce cuál es la página actual y cuántos botones |
||||
debe mostrar. Por ejemplo, |
||||
|
||||
```php |
||||
use yii\widgets\LinkPager; |
||||
|
||||
echo LinkPager::widget([ |
||||
'pagination' => $pagination, |
||||
]); |
||||
``` |
||||
|
||||
Si quieres construir los elementos de UI manualmente, puedes utilizar [[yii\data\Pagination::createUrl()]] para generar URLs que |
||||
dirigirán a las distintas páginas. El método requiere un parámetro de página y generará una URL apropiadamente formada |
||||
contieniendo el parámetro de página. Por ejemplo, |
||||
|
||||
```php |
||||
// especifica la ruta que la URL generada debería utilizar |
||||
// Si no lo especificas, se utilizará la ruta de la petición actual |
||||
$pagination->route = 'article/index'; |
||||
|
||||
// muestra: /index.php?r=article%2Findex&page=100 |
||||
echo $pagination->createUrl(100); |
||||
|
||||
// muestra: /index.php?r=article%2Findex&page=101 |
||||
echo $pagination->createUrl(101); |
||||
``` |
||||
|
||||
> Tip: puedes personalizar el parámetro `page` de la consulta configurando |
||||
la propiedad [[yii\data\Pagination::pageParam|pageParam]] al crear el objeto de la paginación. |
@ -0,0 +1,527 @@
|
||||
Autorización |
||||
============ |
||||
|
||||
Autorización esl el proceso de verificación de que un usuario tenga sugifientes permisos para realizar algo. Yii provee |
||||
dos métodos de autorización: Filtro de Control de Acceso y Control Basado en Roles (ACF y RBAC por sus siglas en inglés). |
||||
|
||||
|
||||
## Filtro de Control de Acceso <span id="access-control-filter"></span> |
||||
|
||||
Filtro de Control de Acceso (ACF) es un único método de autorización implementado como [[yii\filters\AccessControl]], el cual |
||||
es mejor utilizado por aplicaciones que sólo requieran un control de acceso simple. Como su nombre lo indica, ACF es |
||||
un [filtro](structure-filters.md) de acción que puede ser utilizado en un controlador o en un módulo. Cuando un usuario solicita |
||||
la ejecución de una acción, ACF comprobará una lista de [[yii\filters\AccessControl::rules|reglas de acceso]] |
||||
para determinar si el usuario tiene permitido acceder a dicha acción. |
||||
|
||||
El siguiente código muestra cómo utilizar ACF en el controlador `site`: |
||||
|
||||
```php |
||||
use yii\web\Controller; |
||||
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' => ['@'], |
||||
], |
||||
], |
||||
], |
||||
]; |
||||
} |
||||
// ... |
||||
} |
||||
``` |
||||
|
||||
En el código anterior, ACF es adjuntado al controlador `site` en forma de behavior (comportamiento). Esta es la forma típica de utilizar |
||||
un filtro de acción. La opción `only` especifica que el ACF debe ser aplicado solamente a las acciones `login`, `logout` y `signup`. |
||||
Las acciones restantes en el controlador `site` no están sujetas al control de acceso. La opción `rules` lista |
||||
las [[yii\filters\AccessRule|reglas de acceso]], y se lee como a continuación: |
||||
|
||||
- Permite a todos los usuarios invitados (sin autenticar) acceder a las acciones `login` y `signup`. La opción `roles` |
||||
contiene el signo de interrogación `?`, que es un código especial para representar a los "invitados". |
||||
- Permite a los usuarios autenticados acceder a la acción `logout`. El signo `@` es otro código especial que representa |
||||
a los "usuarios autenticados". |
||||
|
||||
ACF ejecuta la comprobación de autorización examinando las reglas de acceso una a una desde arriba hacia abajo hasta que encuentra |
||||
una regla que aplique al contexto de ejecución actual. El valor `allow` de la regla que coincida será entonces utilizado |
||||
para juzgar si el usuario está autorizado o no. Si ninguna de las reglas coincide, significa que el usuario NO está autorizado, |
||||
y el ACF detendrá la ejecución de la acción. |
||||
|
||||
Cuando el ACF determina que un usuario no está autorizado a acceder a la acción actual, toma las siguientes medidas por defecto: |
||||
|
||||
* Si el usuario es un invitado, llamará a [[yii\web\User::loginRequired()]] para redireccionar el navegador a la pantalla de login. |
||||
* Si el usuario está autenticado, lanzará una excepeción [[yii\web\ForbiddenHttpException]]. |
||||
|
||||
Puedes personalizar este comportamiento configurando la propiedad [[yii\filters\AccessControl::denyCallback]] como a continuación: |
||||
|
||||
```php |
||||
[ |
||||
'class' => AccessControl::className(), |
||||
... |
||||
'denyCallback' => function ($rule, $action) { |
||||
throw new \Exception('No tienes los suficientes permisos para acceder a esta página'); |
||||
} |
||||
] |
||||
``` |
||||
|
||||
Las [[yii\filters\AccessRule|Reglas de Acceso]] soportan varias opciones. Abajo hay un resumen de las mismas. |
||||
También puedes extender de [[yii\filters\AccessRule]] para crear tus propias clases de reglas de acceso personalizadas. |
||||
|
||||
* [[yii\filters\AccessRule::allow|allow]]: especifica si la regla es de tipo "allow" (permitir) o "deny" (denegar). |
||||
|
||||
* [[yii\filters\AccessRule::actions|actions]]: especifica con qué acciones coinciden con esta regla. Esta debería ser |
||||
un array de IDs de acciones. La comparación es sensible a mayúsculas. Si la opción está vacía o no definida, |
||||
significa que la regla se aplica a todas las acciones. |
||||
|
||||
* [[yii\filters\AccessRule::controllers|controllers]]: especifica con qué controladores coincide |
||||
esta regla. Esta debería ser un array de IDs de controladores. Cada ID de controlador es prefijado con el ID del módulo (si existe). |
||||
La comparación es sensible a mayúsculas. Si la opción está vacía o no definida, significa que la regla se aplica a todos los controladores. |
||||
|
||||
* [[yii\filters\AccessRule::roles|roles]]: especifica con qué roles de usuarios coincide esta regla. |
||||
Son reconocidos dos roles especiales, y son comprobados vía [[yii\web\User::isGuest]]: |
||||
|
||||
- `?`: coincide con el usuario invitado (sin autenticar) |
||||
- `@`: coincide con el usuario autenticado |
||||
|
||||
El utilizar otro nombre de rol invocará una llamada a [[yii\web\User::can()]], que requiere habilitar RBAC |
||||
(a ser descrito en la próxima subsección). Si la opción está vacía o no definida, significa que la regla se aplica a todos los roles. |
||||
|
||||
* [[yii\filters\AccessRule::ips|ips]]: especifica con qué [[yii\web\Request::userIP|dirección IP del cliente]] coincide esta regla. |
||||
Una dirección IP puede contener el caracter especial `*` al final de manera que coincidan todas las IPs que comiencen igual. |
||||
Por ejemplo, '192.168.*' coincide con las direcciones IP en el segmento '192.168.'. Si la opción está vacía o no definida, |
||||
significa que la regla se aplica a todas las direcciones IP. |
||||
|
||||
* [[yii\filters\AccessRule::verbs|verbs]]: especifica con qué método de la solicitud (por ej. `GET`, `POST`) coincide esta regla. |
||||
La comparación no distingue minúsculas de mayúsculas. |
||||
|
||||
* [[yii\filters\AccessRule::matchCallback|matchCallback]]: especifica una función PHP invocable que debe ser llamada para determinar |
||||
si la regla debe ser aplicada. |
||||
|
||||
* [[yii\filters\AccessRule::denyCallback|denyCallback]]: especifica una función PHP invocable que debe ser llamada cuando esta regla |
||||
deniegue el acceso. |
||||
|
||||
Debajo hay un ejemplo que muestra cómo utilizar la opción `matchCallback`, que te permite escribir lógica de comprabación de acceso |
||||
arbitraria: |
||||
|
||||
```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'; |
||||
} |
||||
], |
||||
], |
||||
], |
||||
]; |
||||
} |
||||
|
||||
// Callback coincidente llamado! Esta página sólo puede ser accedida cada 31 de Octubre |
||||
public function actionSpecialCallback() |
||||
{ |
||||
return $this->render('happy-halloween'); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
|
||||
## Control de Acceso Basado en Roles (RBAC) <span id="rbac"></span> |
||||
|
||||
El Control de Acceso Basado en Roles (RBAC) provee una simple pero poderosa manera centralizada de control de acceso. Por favos consulta |
||||
la [Wikipedia](http://en.wikipedia.org/wiki/Role-based_access_control) para más detalles sobre comparar RBAC |
||||
con otros mecanismos de control de acceso más tradicionales. |
||||
|
||||
Yii implementa una Jerarquía General RBAC, siguiendo el [modelo NIST RBAC](http://csrc.nist.gov/rbac/sandhu-ferraiolo-kuhn-00.pdf). |
||||
Esto provee la funcionalidad RBAC a través de [componente de la aplicación](structure-application-components.md) [[yii\rbac\ManagerInterface|authManager]]. |
||||
|
||||
Utilizar RBAC envuelve dos cosas. La primera es construir los datos de autorización RBAC, y la segunda |
||||
es utilizar esos datos de autorización para comprobar el acceso en los lugares donde se necesite. |
||||
|
||||
Para facilitar la próxima descripción, necesitamos primero instroducir algunos conceptos RBAC básicos. |
||||
|
||||
|
||||
### Conceptos Básicos <span id="basic-concepts"></span> |
||||
|
||||
Un rol representa una colección de *permisos* (por ej. crear posts, actualizar posts). Un rol puede ser asignado |
||||
a uno o varios usuarios. Para comprobar que un usuario cuenta con determinado permiso, podemos comprobar si el usuario tiene asignado |
||||
un rol que cuente con dicho permiso. |
||||
|
||||
Asociado a cada rol o permiso, puede puede haber una *regla*. Una regla representa una porción de código que será |
||||
ejecutada durante la comprobación de acceso para determinar si el rol o permiso correspondiente aplica al usuario actual. |
||||
Por ejemplo, el permiso "actualizar post" puede tener una regla que compruebe que el usuario actual es el autor del post. |
||||
Durante la comprobación de acceso, si el usuario NO es el autor del post, se considerará que el/ella no cuenta con el permiso "actualizar post". |
||||
|
||||
Tanto los roles como los permisos pueden ser organizados en una jerarquía. En particular, un rol puede consistir en otros roles o permisos; |
||||
y un permiso puede consistir en otros permisos. Yii implementa una jerarquía de *orden parcial*, que incluye |
||||
una jerarquía de *árbol* especial. Mientras que un rol puede contener un permiso, esto no sucede al revés. |
||||
|
||||
|
||||
### Configurar RBAC <span id="configuring-rbac"></span> |
||||
|
||||
Antes de definir todos los datos de autorización y ejecutar la comprobación de acceso, necesitamos configurar el |
||||
componente de la aplicación [[yii\base\Application::authManager|authManager]]. Yii provee dos tipos de administradores de autorización: |
||||
[[yii\rbac\PhpManager]] y [[yii\rbac\DbManager]]. El primero utiliza un archivo PHP para almacenar los datos |
||||
de autorización, mientras que el segundo almacena dichos datos en una base de datos. Puedes considerar utilizar el primero si tu aplicación |
||||
no requiere una administración de permisos y roles muy dinámica. |
||||
|
||||
|
||||
#### Utilizar `PhpManager` <span id="using-php-manager"></span> |
||||
|
||||
El siguiente código muestra cómo configurar `authManager` en la configuración de nuestra aplicación utilizando la clase [[yii\rbac\PhpManager]]: |
||||
|
||||
```php |
||||
return [ |
||||
// ... |
||||
'components' => [ |
||||
'authManager' => [ |
||||
'class' => 'yii\rbac\PhpManager', |
||||
], |
||||
// ... |
||||
], |
||||
]; |
||||
``` |
||||
|
||||
El `authManager` ahora puede ser accedido vía `\Yii::$app->authManager`. |
||||
|
||||
Por defecto, [[yii\rbac\PhpManager]] almacena datos RBAC en archivos bajo el directorio `@app/rbac`. Asegúrate de que el directorio |
||||
y todos sus archivos son tienen permiso de escritura para el proceso del servidor Web si la jerarquía de permisos necesita ser modoficada en línea. |
||||
|
||||
|
||||
#### Utilizar `DbManager` <span id="using-db-manager"></span> |
||||
|
||||
El sigiente código muestra cómo configurar `authManager` en la configuración de la aplicación utilizando la clase [[yii\rbac\DbManager]]: |
||||
|
||||
```php |
||||
return [ |
||||
// ... |
||||
'components' => [ |
||||
'authManager' => [ |
||||
'class' => 'yii\rbac\DbManager', |
||||
], |
||||
// ... |
||||
], |
||||
]; |
||||
``` |
||||
> Note: si estás utilizando el template yii2-basic-app, existe el archivo de configuración `config/console.php` donde |
||||
necesita declararse `authManager` adicionalmente a `config/web.php`. |
||||
> En el caso de yii2-advanced-app, `authManager` sólo debe declararse en `common/config/main.php`. |
||||
|
||||
`DbManager` utiliza cuatro tablas de la BD para almacenar los datos: |
||||
|
||||
- [[yii\rbac\DbManager::$itemTable|itemTable]]: la tabla para almacenar los ítems de autorización. Por defecto "auth_item". |
||||
- [[yii\rbac\DbManager::$itemChildTable|itemChildTable]]: la tabla para almacentar la jerarquía de los ítems de autorización. Por defecto "auth_item_child". |
||||
- [[yii\rbac\DbManager::$assignmentTable|assignmentTable]]: la tabla para almacenar las asignaciones de los ítems de autorización. Por defecto "auth_assignment". |
||||
- [[yii\rbac\DbManager::$ruleTable|ruleTable]]: la tabla para almacenar las reglas. Por defecto "auth_rule". |
||||
|
||||
Antes de continuar, necesitas crear las tablas respectivas en la base de datos. Para hacerlo, puedes utilizar las migraciones contenidas en `@yii/rbac/migrations`: |
||||
|
||||
`yii migrate --migrationPath=@yii/rbac/migrations` |
||||
|
||||
El `authManager` puede ahora ser accedido vía `\Yii::$app->authManager`. |
||||
|
||||
|
||||
### Construir los Datos de Autorización <span id="generating-rbac-data"></span> |
||||
|
||||
Construir los datos de autorización implica las siguientes tareas: |
||||
|
||||
- definir roles y permisos; |
||||
- establecer relaciones entre roles y permisos; |
||||
- definir reglas; |
||||
- asociar reglas con roles y permisos; |
||||
- asignar roles a usuarios. |
||||
|
||||
Dependiendo de los requerimientos de flexibilidad en la autorización, las tareas se pueden lograr de diferentes maneras. |
||||
|
||||
Si la jerarquía de permisos no cambia en absoluto y tienes un número fijo de usuarios puede crear un |
||||
[comando de consola](tutorial-console.md#create-command) que va a inicializar los datos de autorización una vez a través de las API que ofrece por `authManager`: |
||||
|
||||
```php |
||||
<?php |
||||
namespace app\commands; |
||||
|
||||
use Yii; |
||||
use yii\console\Controller; |
||||
|
||||
class RbacController extends Controller |
||||
{ |
||||
public function actionInit() |
||||
{ |
||||
$auth = Yii::$app->authManager; |
||||
|
||||
// agrega el permiso "createPost" |
||||
$createPost = $auth->createPermission('createPost'); |
||||
$createPost->description = 'Create a post'; |
||||
$auth->add($createPost); |
||||
|
||||
// agrega el permiso "updatePost" |
||||
$updatePost = $auth->createPermission('updatePost'); |
||||
$updatePost->description = 'Update post'; |
||||
$auth->add($updatePost); |
||||
|
||||
// agrega el rol "author" y le asigna el permiso "createPost" |
||||
$author = $auth->createRole('author'); |
||||
$auth->add($author); |
||||
$auth->addChild($author, $createPost); |
||||
|
||||
// agrega el rol "admin" y le asigna el permiso "updatePost" |
||||
// más los permisos del rol "author" |
||||
$admin = $auth->createRole('admin'); |
||||
$auth->add($admin); |
||||
$auth->addChild($admin, $updatePost); |
||||
$auth->addChild($admin, $author); |
||||
|
||||
// asigna roles a usuarios. 1 y 2 son IDs devueltos por IdentityInterface::getId() |
||||
// usualmente implementado en tu modelo User. |
||||
$auth->assign($author, 2); |
||||
$auth->assign($admin, 1); |
||||
} |
||||
} |
||||
``` |
||||
|
||||
> Note: Si estas utilizando el template avanzado, necesitas poner tu `RbacController` dentro del directorio `console/controllers` |
||||
y cambiar el espacio de nombres a `console/controllers`. |
||||
|
||||
Después de ejecutar el comando `yii rbac/init`, obtendremos la siguiente jerarquía: |
||||
|
||||
![Simple RBAC hierarchy](../guide/images/rbac-hierarchy-1.png "Simple RBAC hierarchy") |
||||
|
||||
"Author" puede crear un post, "admin" puede actualizar posts y hacer todo lo que puede hacer "author". |
||||
|
||||
Si tu aplicación permite el registro de usuarios, necesitas asignar los roles necesarios para cada usuario nuevo. Por ejemplo, para que todos |
||||
los usuarios registrados tengan el rol "author", en el template de aplicación avanzada debes modificar `frontend\models\SignupForm::signup()` |
||||
como a continuación: |
||||
|
||||
```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); |
||||
|
||||
// las siguientes tres líneas fueron agregadas |
||||
$auth = Yii::$app->authManager; |
||||
$authorRole = $auth->getRole('author'); |
||||
$auth->assign($authorRole, $user->getId()); |
||||
|
||||
return $user; |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
``` |
||||
|
||||
Para aplicaciones que requieren un control de acceso complejo con una actualización constante en los datos de autorización, puede ser necesario |
||||
desarrollar una interfaz especial (por ej. un panel de administración) utilizando las APIs ofrecidas por `authManager`. |
||||
|
||||
|
||||
### Utilizar Reglas <span id="using-rules"></span> |
||||
|
||||
Como se había mencionado, las reglas agregan restricciones adicionales a los roles y permisos. Una regla es una clase extendida |
||||
de [[yii\rbac\Rule]]. Debe implementar al método [[yii\rbac\Rule::execute()|execute()]]. En la jerarquía que creamos |
||||
previamente, "author" no puede editar su propio post. Vamos a arreglarlo. Primero necesitamos una regla para comprobar que el usuario actual es el autor del post: |
||||
|
||||
```php |
||||
namespace app\rbac; |
||||
|
||||
use yii\rbac\Rule; |
||||
|
||||
/** |
||||
* Comprueba si authorID coincide con el usuario pasado como parámetro |
||||
*/ |
||||
class AuthorRule extends Rule |
||||
{ |
||||
public $name = 'isAuthor'; |
||||
|
||||
/** |
||||
* @param string|integer $user el ID de usuario. |
||||
* @param Item $item el rol o permiso asociado a la regla |
||||
* @param array $params parámetros pasados a ManagerInterface::checkAccess(). |
||||
* @return boolean un valor indicando si la regla permite al rol o permiso con el que está asociado. |
||||
*/ |
||||
public function execute($user, $item, $params) |
||||
{ |
||||
return isset($params['post']) ? $params['post']->createdBy == $user : false; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
La regla anterior comprueba si el `post` fue creado por `$user`. Crearemos un permiso especial, `updateOwnPost`, en el comando que hemos utilizado |
||||
anteriormente: |
||||
|
||||
```php |
||||
$auth = Yii::$app->authManager; |
||||
|
||||
// agrega la regla |
||||
$rule = new \app\rbac\AuthorRule; |
||||
$auth->add($rule); |
||||
|
||||
// agrega el permiso "updateOwnPost" y le asocia la regla. |
||||
$updateOwnPost = $auth->createPermission('updateOwnPost'); |
||||
$updateOwnPost->description = 'Update own post'; |
||||
$updateOwnPost->ruleName = $rule->name; |
||||
$auth->add($updateOwnPost); |
||||
|
||||
// "updateOwnPost" será utilizado desde "updatePost" |
||||
$auth->addChild($updateOwnPost, $updatePost); |
||||
|
||||
// permite a "author" editar sus propios posts |
||||
$auth->addChild($author, $updateOwnPost); |
||||
``` |
||||
|
||||
Ahora tenemos la siguiente jerarquía: |
||||
|
||||
![RBAC hierarchy with a rule](../guide/images/rbac-hierarchy-2.png "RBAC hierarchy with a rule") |
||||
|
||||
|
||||
### Comprobación de Acceso <span id="access-check"></span> |
||||
|
||||
Con los datos de autorización listos, la comprobación de acceso se hace con una simple llamada al método [[yii\rbac\ManagerInterface::checkAccess()]]. |
||||
Dado que la mayoría de la comprobación de acceso se hace sobre el usuario actual, para mayor comodidad Yii proporciona el atajo |
||||
[[yii\web\User::can()]], que puede ser utilizado como a continuación: |
||||
|
||||
```php |
||||
if (\Yii::$app->user->can('createPost')) { |
||||
// crear el post |
||||
} |
||||
``` |
||||
|
||||
Si el usuario actual es Jane con `ID=1`, comenzamos desde `createPost` y tratamos de alcanzar a `Jane`: |
||||
|
||||
![Access check](../guide/images/rbac-access-check-1.png "Access check") |
||||
|
||||
Con el fin de comprobar si un usuario puede actualizar un post, necesitamos pasarle un parámetro adicional requerido por `AuthorRule`, descrito antes: |
||||
|
||||
```php |
||||
if (\Yii::$app->user->can('updatePost', ['post' => $post])) { |
||||
// actualizar post |
||||
} |
||||
``` |
||||
|
||||
Aquí es lo que sucede si el usuario actual es John: |
||||
|
||||
|
||||
![Access check](../guide/images/rbac-access-check-2.png "Access check") |
||||
|
||||
Comenzamos desde `updatePost` y pasamos por `updateOwnPost`. Con el fin de pasar la comprobación de acceso, `AuthorRule` |
||||
debe devolver `true` desde su método `execute()`. El método recive `$params` desde la llamada al método `can()`, cuyo valor es |
||||
`['post' => $post]`. Si todo está bien, vamos a obtener `author`, el cual es asignado a John. |
||||
|
||||
En caso de Jane es un poco más simple, ya que ella es un "admin": |
||||
|
||||
![Access check](../guide/images/rbac-access-check-3.png "Access check") |
||||
|
||||
|
||||
### Utilizar Roles por Defecto <span id="using-default-roles"></span> |
||||
|
||||
Un rol por defecto es un rol que esta asignado *implícitamente* a *todos* los usuarios. La llamada a [[yii\rbac\ManagerInterface::assign()]] |
||||
no es necesaria, y los datos de autorización no contienen su información de asignación. |
||||
|
||||
Un rol por defecto es usualmente asociado con una regla que determina si el rol aplica al usuario siendo verificado. |
||||
|
||||
Los roles por defecto se utilizan a menudo en aplicaciones que ya tienen algún tipo de asignación de roles. Por ejemplo, una aplicación |
||||
puede tener una columna "grupo" en su tabla de usuario para representar a qué grupo de privilegio pertenece cada usuario. |
||||
Si cada grupo privilegio puede ser conectado a un rol de RBAC, se puede utilizar la función de rol por defecto para asignar |
||||
cada usuario a un rol RBAC automáticamente. Usemos un ejemplo para mostrar cómo se puede hacer esto. |
||||
|
||||
Suponga que en la tabla de usuario, usted tiene una columna `group` que utiliza 1 para representar el grupo administrador y 2 al grupo autor. |
||||
Planeas tener dos roles RBAC, `admin` y `author`, para representar los permisos de estos dos grupos, respectivamente. |
||||
Puede configurar los datos RBAC de la siguiente manera, |
||||
|
||||
|
||||
```php |
||||
namespace app\rbac; |
||||
|
||||
use Yii; |
||||
use yii\rbac\Rule; |
||||
|
||||
/** |
||||
* Comprueba si el grupo coincide |
||||
*/ |
||||
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); |
||||
// ... agrega permisos hijos a $author ... |
||||
|
||||
$admin = $auth->createRole('admin'); |
||||
$admin->ruleName = $rule->name; |
||||
$auth->add($admin); |
||||
$auth->addChild($admin, $author); |
||||
// ... agrega permisos hijos a $admin ... |
||||
``` |
||||
|
||||
Tenga en cuenta que en el ejemplo anterior, dado que "author" es agregado como hijo de "admin", cuando implementes el método `execute()` |
||||
de la clase de la regla, necesitas respetar esta jerarquía. Esto se debe a que cuando el nombre del rol es "author", |
||||
el método `execute()` devolverá true si el grupo de usuario es tanto 1 como 2 (lo que significa que el usuario se encuentra en |
||||
cualquiera de los dos grupos, "admin" o "author"). |
||||
|
||||
Luego, configura `authManager` enumerando los dos roles en [[yii\rbac\BaseManager::$defaultRoles]]: |
||||
|
||||
```php |
||||
return [ |
||||
// ... |
||||
'components' => [ |
||||
'authManager' => [ |
||||
'class' => 'yii\rbac\PhpManager', |
||||
'defaultRoles' => ['admin', 'author'], |
||||
], |
||||
// ... |
||||
], |
||||
]; |
||||
``` |
||||
|
||||
Ahora si realizas una comprobación de acceso, tanto el rol `admin` y como el rol `author` serán comprobados evaluando |
||||
las reglas asociadas con ellos. Si la regla devuelve true, significa que la regla aplica al usuario actual. |
||||
Basado en la implementación de la regla anterior, esto significa que si el valor `group` en un usuario es 1, el rol `admin` |
||||
se aplicaría al usuario; y si el valor de `group` es 2, se le aplicaría el rol `author`. |
@ -0,0 +1,31 @@
|
||||
Trabajar con Passwords |
||||
====================== |
||||
|
||||
La mayoría de los desarrolladores saben que los passwords no deben ser guardados en texto plano, pero muchos desarrolladores aún creen |
||||
que es seguro aplicar a los passowrds hash `md5` o `sha1`. Hubo un tiempo cuando utilizar esos algoritmos de hash mencionados era suficiente, |
||||
pero el hardware moderno hace posible que ese tipo de hash e incluso más fuertes, puedan revertirse rápidamente utilizando ataques de fuerza bruta. |
||||
|
||||
Para poder proveer de una seguridad mayor para los passwords de los usuarios, incluso en el peor de los escenarios (tu aplicación sufre una brecha de seguridad), |
||||
necesitas utilizar un algoritmo que resista los ataques de fuerza bruta. La mejor elección actualmente es `bcrypt`. |
||||
En PHP, puedes generar un hash `bcrypt` utilizando la [función crypt](http://php.net/manual/en/function.crypt.php). Yii provee |
||||
dos funciones auxiliares que hacen que `crypt` genere y verifique los hash más fácilmente. |
||||
|
||||
Cuando un usuario provee un password por primera vez (por ej., en la registración), dicho password necesita ser pasado por un hash: |
||||
|
||||
|
||||
```php |
||||
$hash = Yii::$app->getSecurity()->generatePasswordHash($password); |
||||
``` |
||||
|
||||
El hash puede estar asociado con el atributo del model correspondiente, de manera que pueda ser almacenado en la base de datos para uso posterior. |
||||
|
||||
Cuando un usuario intenta ingresar al sistema, el password enviado debe ser verificado con el password con hash almacenado previamente: |
||||
|
||||
|
||||
```php |
||||
if (Yii::$app->getSecurity()->validatePassword($password, $hash)) { |
||||
// todo en orden, dejar ingresar al usuario |
||||
} else { |
||||
// password erróneo |
||||
} |
||||
``` |
@ -1,32 +1,35 @@
|
||||
Mirando Hacia Adelante |
||||
====================== |
||||
|
||||
Hasta ahora, has creado una aplicación completa en Yii, y has aprendido cómo implementar algunas de las características más típicas y necesarias, como la de obtener datos de los usuarios a través de un formulario HTML, y traer datos de la base de datos |
||||
para mostrarlos en forma paginada. También has aprendido cómo utilizar la herramienta [Gii](tool-gii.md) para generar |
||||
código automáticamente, lo que transforma el hecho de programar en una tarea tan simple como la de completar algunos formularios. |
||||
En esta sección, resumiremos los recursos acerca de Yii que ayudan a ser más productivos al utilizar la librería. |
||||
Si has leído el capítulo "Comenzando con Yii" completo, has creado una aplicación completa en Yii. En el proceso, has aprendido cómo implementar algunas |
||||
características comúnmente necesitadas, tales como obtener datos del usuario a través de formularios HTML, traer datos desde la base de datos, |
||||
y mostrar datos utilizando paginación. También has aprendido a utilizar [Gii](https://github.com/yiisoft/yii2-gii/blob/master/docs/guide/README.md) para generar |
||||
código automáticamente. Utilizar Gii para la generación de código transforma la carga en el proceso de tu desarrollo Web en una tarea tan simple como solamente completar unos formularios. |
||||
|
||||
Esta sección resumirá los recursos disponibles de Yii que te ayudarán a ser más productivo al utilizar el framework. |
||||
|
||||
* Documentación |
||||
- La Guía Definitiva: |
||||
Como el nombre indica, la guía precisamente define cómo Yii debería trabajar y te da una guía general |
||||
acerca de cómo usar la librería. Este es el tutorial simple más importante de Yii que deberías leer |
||||
antes de empezar a escribir código. |
||||
- La Referencia de Clases: |
||||
Este especifica el uso de cada clase provista por Yii. Debería ser utilizado mayormente cuando estés escribiendo |
||||
código y quieras entender el funcionamiento de alguna clase, método o propiedad en particular. |
||||
- Artículos de la Wiki: |
||||
Estos artículos son escritos por usuarios de Yii basado en experiencias propias. En su mayoría son escritos |
||||
como recetas que muestran cómo resolver problemas particulares en Yii. Aunque la calidad de estos artículos |
||||
puede ser tan buena como la Guía Definitiva, son particularmente útiles al cubrir aspectos más amplios |
||||
y puede a menudo ofrecer soluciones listas para usar. |
||||
- Libros |
||||
- [La Guía Definitiva](http://www.yiiframework.com/doc-2.0/guide-README.html): |
||||
Como su nombre lo indica, la guía define precisamente cómo debería trabajar Yii y provee guías generales |
||||
acerca de su utilización. Es el tutorial más importante de Yii, y el que deberías leer |
||||
antes de escribir cualquier código en Yii. |
||||
- [La Referencia de Clases](http://www.yiiframework.com/doc-2.0/index.html): |
||||
Esta especifica el uso de cada clase provista por Yii. Debería ser utilizada principalmente cuando estás escribiendo |
||||
código y deseas entender el uso de una clase, método o propiedad en particular. El uso de la referencia de clases es mejor luego de un entendimiento contextual del framework. |
||||
- [Los Artículos de la Wiki](http://www.yiiframework.com/wiki/?tag=yii2): |
||||
Los artículos de la wiki son escritos por usuarios de Yii basados en sus propias experiencias. La mayoría de ellos están escritos |
||||
como recetas de cocina, y muestran cómo resolver problemas particulares utilizando Yii. Si bien la calidad de estos |
||||
puede no ser tan buena como la de la Guía Definitiva, son útiles ya que cubren un espectro muy amplio |
||||
de temas y puede proveer a menudo soluciones listas para usar. |
||||
- [Libros](http://www.yiiframework.com/doc/) |
||||
* [Extensiones](http://www.yiiframework.com/extensions/): |
||||
Yii cuenta con una librería de cientos de extensiones que han sido provistas por la comunidad de usuarios que pueden ser fácilmente integradas |
||||
en tu aplicación y lograr que sea más simple y rápido desarrollarla. |
||||
Yii puede hacer alarde de una librería de miles de extensiones contribuidas por usuarios, que pueden fácilmente conectadas a tu aplicación, haciendo que el desarrollo de la misma sea todavía más fácil y rápido. |
||||
* Comunidad |
||||
- [Foro](http://www.yiiframework.com/forum/) |
||||
- [GitHub](https://github.com/yiisoft/yii2) |
||||
- [Facebook](https://www.facebook.com/groups/yiitalk/) |
||||
- [Twitter](https://twitter.com/yiiframework) |
||||
- [LinkedIn](https://www.linkedin.com/groups/yii-framework-1483367) |
||||
|
||||
- Foro: <http://www.yiiframework.com/forum/> |
||||
- Chat IRC: El canal #yii en la red freenode (<irc://irc.freenode.net/yii>) |
||||
- Chat Gitter: <https://gitter.im/yiisoft/yii2> |
||||
- GitHub: <https://github.com/yiisoft/yii2> |
||||
- Facebook: <https://www.facebook.com/groups/yiitalk/> |
||||
- Twitter: <https://twitter.com/yiiframework> |
||||
- LinkedIn: <https://www.linkedin.com/groups/yii-framework-1483367> |
||||
- Stackoverflow: <http://stackoverflow.com/questions/tagged/yii2> |
||||
|
@ -0,0 +1,49 @@
|
||||
Preparación del entorno de test |
||||
=============================== |
||||
|
||||
> Note: Esta sección se encuentra en desarrollo. |
||||
|
||||
Yii 2 ha mantenido integración oficial con el framework de testing [`Codeception`](https://github.com/Codeception/Codeception), |
||||
que te permite crear los siguientes tipos de tests: |
||||
|
||||
- [Test de unidad](test-unit.md) - verifica que una unidad simple de código funciona como se espera; |
||||
- [Test funcional](test-functional.md) - verifica escenarios desde la perspectiva de un usuario a través de la emulación de un navegador; |
||||
- [Test de aceptación](test-acceptance.md) - verifica escenarios desde la perspectiva de un usuario en un navegador. |
||||
|
||||
Yii provee grupos de pruebas listos para utilizar en ambos |
||||
[`yii2-basic`](https://github.com/yiisoft/yii2-app-basic) y |
||||
[`yii2-advanced`](https://github.com/yiisoft/yii2-app-advanced) templates de proyectos. |
||||
|
||||
Para poder ejecutar estos tests es necesario instalar [Codeception](https://github.com/Codeception/Codeception). |
||||
Puedes instalarlo tanto localmente - únicamente para un proyecto en particular, o globalmente - para tu máquina de desarrollo. |
||||
|
||||
Para la instalación local utiliza los siguientes comandos: |
||||
|
||||
``` |
||||
composer require "codeception/codeception=2.1.*" |
||||
composer require "codeception/specify=*" |
||||
composer require "codeception/verify=*" |
||||
``` |
||||
|
||||
Para la instalación global necesitarás la directiva `global`: |
||||
|
||||
``` |
||||
composer global require "codeception/codeception=2.1.*" |
||||
composer global require "codeception/specify=*" |
||||
composer global require "codeception/verify=*" |
||||
``` |
||||
|
||||
En caso de que nunca hayas utilizado Composer para paquetes globales, ejecuta `composer global status`. Esto debería mostrar la salida: |
||||
|
||||
``` |
||||
Changed current directory to <directory> |
||||
``` |
||||
|
||||
Entonces agrega `<directory>/vendor/bin` a tu variable de entorno `PATH`. Ahora podrás utilizar el `codecept` en la línea |
||||
de comandos a nivel global. |
||||
|
||||
> Note: la instalación global te permite usar Codeception para todos los proyectos en los que trabajes en tu máquina de desarrollo y |
||||
te permite ejecutar el comando `codecept` globalmente sin especificar su ruta. De todos modos, ese acercamiento podría ser inapropiado, |
||||
por ejemplo, si 2 proyectos diferentes requieren diferentes versiones de Codeception instaladas. |
||||
Por simplicidad, todos los comandos relacionados a tests en esta guía están escritos asumiendo que Codeception |
||||
ha sido instalado en forma global. |
@ -0,0 +1,379 @@
|
||||
Fixtures |
||||
======== |
||||
|
||||
Los fixtures son una parte importante de los tests. Su propósito principal es el de preparar el entorno en una estado fijado/conocido |
||||
de manera que los tests sean repetibles y corran de la manera esperada. Yii provee un framework de fixtures que te permite |
||||
dichos fixtures de manera precisa y usarlo de forma simple. |
||||
|
||||
Un concepto clave en el framework de fixtures de Yii es el llamado *objeto fixture*. Un objeto fixture representa |
||||
un aspecto particular de un entorno de pruebas y es una instancia de [[yii\test\Fixture]] o heredada de esta. Por ejemplo, |
||||
puedes utilizar `UserFixture` para asegurarte de que la tabla de usuarios de la BD contiene un grupo de datos fijos. Entonces cargas uno o varios |
||||
objetos fixture antes de correr un test y lo descargas cuando el test ha concluido. |
||||
|
||||
Un fixture puede depender de otros fixtures, especificándolo en su propiedad [[yii\test\Fixture::depends]]. |
||||
Cuando un fixture está siendo cargado, los fixtures de los que depende serán cargados automáticamente ANTES que él; |
||||
y cuando el fixture está siendo descargado, los fixtures dependientes serán descargados DESPUÉS de él. |
||||
|
||||
|
||||
Definir un Fixture |
||||
------------------ |
||||
|
||||
Para definir un fixture, crea una nueva clase que extienda de [[yii\test\Fixture]] o [[yii\test\ActiveFixture]]. |
||||
El primero es más adecuado para fixtures de propósito general, mientras que el último tiene características mejoradas específicamente |
||||
diseñadas para trabajar con base de datos y ActiveRecord. |
||||
|
||||
El siguiente código define un fixture acerca del ActiveRecord `User` y su correspondiente tabla user. |
||||
|
||||
```php |
||||
<?php |
||||
namespace app\tests\fixtures; |
||||
|
||||
use yii\test\ActiveFixture; |
||||
|
||||
class UserFixture extends ActiveFixture |
||||
{ |
||||
public $modelClass = 'app\models\User'; |
||||
} |
||||
``` |
||||
|
||||
> Tip: Cada `ActiveFixture` se encarga de preparar la tabla de la DB para los tests. Puedes especificar la tabla |
||||
> definiendo tanto la propiedad [[yii\test\ActiveFixture::tableName]] o la propiedad [[yii\test\ActiveFixture::modelClass]]. |
||||
> Haciéndolo como el último, el nombre de la tabla será tomado de la clase `ActiveRecord` especificada en `modelClass`. |
||||
|
||||
> Note: [[yii\test\ActiveFixture]] es sólo adecualdo para bases de datos SQL. Para bases de datos NoSQL, Yii provee |
||||
> las siguientes clases `ActiveFixture`: |
||||
> |
||||
> - Mongo DB: [[yii\mongodb\ActiveFixture]] |
||||
> - Elasticsearch: [[yii\elasticsearch\ActiveFixture]] (desde la versión 2.0.2) |
||||
|
||||
|
||||
Los datos para un fixture `ActiveFixture` son usualmente provistos en un archivo ubicado en `FixturePath/data/TableName.php`, |
||||
donde `FixturePath` corresponde al directorio conteniendo el archivo de clase del fixture, y `TableName` |
||||
es el nombre de la tabla asociada al fixture. En el ejemplo anterior, el archivo debería ser |
||||
`@app/tests/fixtures/data/user.php`. El archivo de datos debe devolver un array de registros |
||||
a ser insertados en la tabla user. Por ejemplo, |
||||
|
||||
```php |
||||
<?php |
||||
return [ |
||||
'user1' => [ |
||||
'username' => 'lmayert', |
||||
'email' => 'strosin.vernice@jerde.com', |
||||
'auth_key' => 'K3nF70it7tzNsHddEiq0BZ0i-OU8S3xV', |
||||
'password' => '$2y$13$WSyE5hHsG1rWN2jV8LRHzubilrCLI5Ev/iK0r3jRuwQEs2ldRu.a2', |
||||
], |
||||
'user2' => [ |
||||
'username' => 'napoleon69', |
||||
'email' => 'aileen.barton@heaneyschumm.com', |
||||
'auth_key' => 'dZlXsVnIDgIzFgX4EduAqkEPuphhOh9q', |
||||
'password' => '$2y$13$kkgpvJ8lnjKo8RuoR30ay.RjDf15bMcHIF7Vz1zz/6viYG5xJExU6', |
||||
], |
||||
]; |
||||
``` |
||||
|
||||
Puedes dar un alias al registro tal que más tarde en tu test, puedas referirte a ese registra a través de dicho alias. En el ejemplo anterior, |
||||
los dos registros tienen como alias `user1` y `user2`, respectivamente. |
||||
|
||||
Además, no necesitas especificar los datos de columnas auto-incrementales. Yii automáticamente llenará esos valores |
||||
dentro de los registros cuando el fixture está siendo cargado. |
||||
|
||||
> Tip: Puedes personalizar la ubicación del archivo de datos definiendo la propiedad [[yii\test\ActiveFixture::dataFile]]. |
||||
> Puedes también sobrescribir [[yii\test\ActiveFixture::getData()]] para obtener los datos. |
||||
|
||||
Como se describió anteriormente, un fixture puede depender de otros fixtures. Por ejemplo, un `UserProfileFixture` puede necesitar depender de `UserFixture` |
||||
porque la table de perfiles de usuarios contiene una clave foránea a la tabla user. |
||||
La dependencia es especificada vía la propiedad [[yii\test\Fixture::depends]], como a continuación, |
||||
|
||||
```php |
||||
namespace app\tests\fixtures; |
||||
|
||||
use yii\test\ActiveFixture; |
||||
|
||||
class UserProfileFixture extends ActiveFixture |
||||
{ |
||||
public $modelClass = 'app\models\UserProfile'; |
||||
public $depends = ['app\tests\fixtures\UserFixture']; |
||||
} |
||||
``` |
||||
|
||||
La dependencia también asegura que los fixtures son cargados y descargados en un orden bien definido. En el ejemplo `UserFixture` |
||||
será siempre cargado antes de `UserProfileFixture` para asegurar que todas las referencias de las claves foráneas existan y será siempre descargado después de `UserProfileFixture` |
||||
por la misma razón. |
||||
|
||||
Arriba te mostramos cómo definir un fixture de BD. Para definir un fixture no relacionado a BD |
||||
(por ej. un fixture acerca de archivos y directorios), puedes extender de la clase base más general |
||||
[[yii\test\Fixture]] y sobrescribir los métodos [[yii\test\Fixture::load()|load()]] y [[yii\test\Fixture::unload()|unload()]]. |
||||
|
||||
|
||||
Utilizar Fixtures |
||||
----------------- |
||||
|
||||
Si estás utilizando [Codeception](http://codeception.com/) para hacer tests de tu código, deberías considerar el utilizar |
||||
la extensión `yii2-codeception`, que tiene soporte incorporado para la carga y acceso a fixtures. |
||||
En caso de que utilices otros frameworks de testing, puedes usar [[yii\test\FixtureTrait]] en tus casos de tests |
||||
para alcanzar el mismo objetivo. |
||||
|
||||
A continuación describiremos cómo escribir una clase de test de unidad `UserProfile` utilizando `yii2-codeception`. |
||||
|
||||
En tu clase de test de unidad que extiende de [[yii\codeception\DbTestCase]] o [[yii\codeception\TestCase]], |
||||
indica cuáles fixtures quieres utilizar en el método [[yii\test\FixtureTrait::fixtures()|fixtures()]]. Por ejemplo, |
||||
|
||||
```php |
||||
namespace app\tests\unit\models; |
||||
|
||||
use yii\codeception\DbTestCase; |
||||
use app\tests\fixtures\UserProfileFixture; |
||||
|
||||
class UserProfileTest extends DbTestCase |
||||
{ |
||||
public function fixtures() |
||||
{ |
||||
return [ |
||||
'profiles' => UserProfileFixture::className(), |
||||
]; |
||||
} |
||||
|
||||
// ...métodos de test... |
||||
} |
||||
``` |
||||
|
||||
Los fixtures listados en el método `fixtures()` serán automáticamente cargados antes de correr cada método de test |
||||
en el caso de test y descargado al finalizar cada uno. También, como describimos antes, cuando un fixture está |
||||
siendo cargado, todos sus fixtures dependientes serán cargados primero. En el ejemplo de arriba, debido a que |
||||
`UserProfileFixture` depende de `UserFixture`, cuando ejecutas cualquier método de test en la clase, |
||||
dos fixtures serán cargados secuencialmente: `UserFixture` y `UserProfileFixture`. |
||||
|
||||
Al especificar fixtures en `fixtures()`, puedes utilizar tanto un nombre de clase o un array de configuración para referirte a |
||||
un fixture. El array de configuración te permitirá personalizar las propiedades del fixture cuando este es cargado. |
||||
|
||||
Puedes también asignarles alias a los fixtures. En el ejemplo anterior, el `UserProfileFixture` tiene como alias `profiles`. |
||||
En los métodos de test, puedes acceder a un objeto fixture utilizando su alias. Por ejemplo, `$this->profiles` |
||||
devolverá el objeto `UserProfileFixture`. |
||||
|
||||
Dado que `UserProfileFixture` extiende de `ActiveFixture`, puedes por lo tanto usar la siguiente sintáxis para acceder |
||||
a los datos provistos por el fixture: |
||||
|
||||
```php |
||||
// devuelve el registro del fixture cuyo alias es 'user1' |
||||
$row = $this->profiles['user1']; |
||||
// devuelve el modelo UserProfile correspondiente al registro cuyo alias es 'user1' |
||||
$profile = $this->profiles('user1'); |
||||
// recorre cada registro en el fixture |
||||
foreach ($this->profiles as $row) ... |
||||
``` |
||||
|
||||
> Info: `$this->profiles` es todavía del tipo `UserProfileFixture`. Las características de acceso mostradas arriba son implementadas |
||||
> a través de métodos mágicos de PHP. |
||||
|
||||
|
||||
Definir y Utilizar Fixtures Globales |
||||
------------------------------------ |
||||
|
||||
Los fixtures descritos arriba son principalmente utilizados para casos de tests individuales. En la mayoría de los casos, puedes necesitar algunos |
||||
fixtures globales que sean aplicados a TODOS o muchos casos de test. Un ejemplo sería [[yii\test\InitDbFixture]], que hace |
||||
dos cosas: |
||||
|
||||
* Realiza alguna tarea de inicialización común al ejectutar un script ubicado en `@app/tests/fixtures/initdb.php`; |
||||
* Deshabilita la comprobación de integridad antes de cargar otros fixtures de BD, y la rehabilita después de que todos los fixtures son descargados. |
||||
|
||||
Utilizar fixtures globales es similar a utilizar los no-globales. La única diferencia es que declaras estos fixtures |
||||
en [[yii\codeception\TestCase::globalFixtures()]] en vez de en `fixtures()`. Cuando un caso de test carga fixtures, |
||||
primero carga los globales y luego los no-globales. |
||||
|
||||
Por defecto, [[yii\codeception\DbTestCase]] ya declara `InitDbFixture` en su método `globalFixtures()`. |
||||
Esto significa que sólo necesitas trabajar con `@app/tests/fixtures/initdb.php` si quieres realizar algún trabajo de inicialización |
||||
antes de cada test. Sino puedes simplemente enfocarte en desarrollar cada caso de test individual y sus fixtures correspondientes. |
||||
|
||||
|
||||
Organizar Clases de Fixtures y Archivos de Datos |
||||
------------------------------------------------ |
||||
|
||||
Por defecto, las clases de fixtures busca los archivos de datos correspondientes dentro de la carpeta `data`, que es una subcarpeta |
||||
de la carpeta conteniendo los archivos de clases de fixtures. Puedes seguir esta convención al trabajar en proyectos simples. |
||||
Para proyectos más grandes, es probable que a menudo necesites intercambiar entre diferentes archivos de datos para la misma clase de fixture |
||||
en diferentes tests. Recomendamos que organices los archivos de datos en forma jerárquica similar |
||||
a tus espacios de nombre de clases. Por ejemplo, |
||||
|
||||
``` |
||||
# bajo la carpeta tests\unit\fixtures |
||||
|
||||
data\ |
||||
components\ |
||||
fixture_data_file1.php |
||||
fixture_data_file2.php |
||||
... |
||||
fixture_data_fileN.php |
||||
models\ |
||||
fixture_data_file1.php |
||||
fixture_data_file2.php |
||||
... |
||||
fixture_data_fileN.php |
||||
# y así sucesivamente |
||||
``` |
||||
|
||||
De esta manera evitarás la colisión de archivos de datos de fixtures entre tests y podrás utlilizarlos como necesites. |
||||
|
||||
> Note: En el ejemplo de arriba los archivos de fixtures son nombrados así sólo como ejemplo. En la vida real deberías nombrarlos |
||||
> de acuerdo a qué clase de fixture extienden tus clases de fixtures. Por ejemplo, si estás extendiendo |
||||
> de [[yii\test\ActiveFixture]] para fixtures de BD, deberías utilizar nombres de tabla de la BD como nombres de los archivos de fixtures; |
||||
> Si estás extendiendo de [[yii\mongodb\ActiveFixture]] para fixtures de MongoDB, deberías utilizar nombres de colecciones para los nombres de archivo. |
||||
|
||||
Se puede utilizar una jerarquía similar para organizar archivos de clases de fixtures. En vez de utilizar `data` como directorio raíz, podrías |
||||
querer utilizar `fixtures` como directorio raíz para evitar conflictos con los archivos de datos. |
||||
|
||||
|
||||
Resumen |
||||
------- |
||||
|
||||
> Note: Esta sección se encuentra en desarrollo. |
||||
|
||||
Arriba, definimos cómo definir y utilizar fixtures. Abajo resumiremos el típico flujo de trabajo |
||||
de correr tests de unidad relacionados a BD: |
||||
|
||||
1. Usa la herramienta `yii migrate` para actualizar tu base de datos de prueba a la última versión; |
||||
2. Corre el caso de test: |
||||
- Carga los fixtures: limpia las tablas de la BD relevantes y cargala con los datos de los fixtures; |
||||
- Realiza el test en sí; |
||||
- Descarga los fixtures. |
||||
3. Repite el Paso 2 hasta que todos los tests terminen. |
||||
|
||||
|
||||
**Lo siguiente, a ser limpiado** |
||||
|
||||
Administrar Fixtures |
||||
==================== |
||||
|
||||
> Note: Esta sección está en desarrollo. |
||||
> |
||||
> todo: este tutorial podría ser unificado con la parte de arriba en test-fixtures.md |
||||
|
||||
Los fixtures son una parte importante del testing. Su principal propósito es el de poblarte con datos necesarios para el test |
||||
de diferentes casos. Con estos datos. utilizar tests se vuelve más eficiente y útil. |
||||
|
||||
Yii soporta fixtures a través de la herramienta de línea de comandos `yii fixture`. Esta herramienta soporta: |
||||
|
||||
* Cargar fixtures a diferentes almacenamientos: RDBMS, NoSQL, etc; |
||||
* Descargar fixtures de diferentes maneras (usualmente limpiando el almacenamiento); |
||||
* Auto-generar fixtures y poblarlos con datos al azar. |
||||
|
||||
Formato de Fixtures |
||||
------------------- |
||||
|
||||
Los fixtures son objetos con diferentes métodos y configuraciones, inspecciónalos en la [documentación oficial](https://github.com/yiisoft/yii2/blob/master/docs/guide-es/test-fixtures.md). |
||||
Asumamos que tenemos datos de fixtures a cargar: |
||||
|
||||
``` |
||||
#archivo users.php bajo la ruta de los fixtures, por defecto @tests\unit\fixtures\data |
||||
|
||||
return [ |
||||
[ |
||||
'name' => 'Chase', |
||||
'login' => 'lmayert', |
||||
'email' => 'strosin.vernice@jerde.com', |
||||
'auth_key' => 'K3nF70it7tzNsHddEiq0BZ0i-OU8S3xV', |
||||
'password' => '$2y$13$WSyE5hHsG1rWN2jV8LRHzubilrCLI5Ev/iK0r3jRuwQEs2ldRu.a2', |
||||
], |
||||
[ |
||||
'name' => 'Celestine', |
||||
'login' => 'napoleon69', |
||||
'email' => 'aileen.barton@heaneyschumm.com', |
||||
'auth_key' => 'dZlXsVnIDgIzFgX4EduAqkEPuphhOh9q', |
||||
'password' => '$2y$13$kkgpvJ8lnjKo8RuoR30ay.RjDf15bMcHIF7Vz1zz/6viYG5xJExU6', |
||||
], |
||||
]; |
||||
``` |
||||
Si estamos utilizando un fixture que carga datos en la base de datos, entonces esos registros serán insertados en la tabla `users`. Si estamos utilizando fixtures no sql, por ejemplo de `mongodb`, |
||||
entonces estos datos serán aplicados a la colección mongodb `users`. Para aprender cómo implementar varias estrategias de carga y más, visita la [documentación oficial](https://github.com/yiisoft/yii2/blob/master/docs/guide-es/test-fixtures.md). |
||||
El fixture de ejemplo de arriba fue autogenerado por la extensión `yii2-faker`, lee más acerca de esto en su [sección](#auto-generating-fixtures). |
||||
Los nombres de clase de fixtures no deberían ser en plural. |
||||
|
||||
Cargar fixtures |
||||
---------------- |
||||
|
||||
Las clases de fixture deberían tener el prefijo `Fixture`. Por defecto los fixtures serán buscados bajo el espacio de nombre `tests\unit\fixtures`, puedes |
||||
modificar este comportamiento con opciones de comando o configuración. Puedes excluir algunos fixtures para carga o descarga especificando `-` antes de su nombre, por ejemplo `-User`. |
||||
|
||||
Para cargar un fixture, ejecuta el siguiente comando: |
||||
|
||||
``` |
||||
yii fixture/load <fixture_name> |
||||
``` |
||||
|
||||
El parámetro requerido `fixture_name` especifica un nombre de fixture cuyos datos serán cargados. Puedes cargar varios fixtures de una sola vez. |
||||
Abajo se muestran formatos correctos de este comando: |
||||
|
||||
``` |
||||
// carga el fixture `User` |
||||
yii fixture/load User |
||||
|
||||
// lo mismo que arriba, dado que la acción por defecto del comando "fixture" es "load" |
||||
yii fixture User |
||||
|
||||
// carga varios fixtures |
||||
yii fixture User UserProfile |
||||
|
||||
// carga todos los fixtures |
||||
yii fixture/load "*" |
||||
|
||||
// lo mismo que arriba |
||||
yii fixture "*" |
||||
|
||||
// carga todos los fixtures excepto uno |
||||
yii fixture "*" -DoNotLoadThisOne |
||||
|
||||
// carga fixtures, pero los busca en diferente espacio de nombre. El espacio de nombre por defecto es: tests\unit\fixtures. |
||||
yii fixture User --namespace='alias\my\custom\namespace' |
||||
|
||||
// carga el fixture global `some\name\space\CustomFixture` antes de que otros fixtures sean cargados. |
||||
// Por defecto está opción se define como `InitDbFixture` para habilitar/deshabilitar la comprobación de integridad. Puedes especificar varios |
||||
// fixtures globales separados por coma. |
||||
yii fixture User --globalFixtures='some\name\space\Custom' |
||||
``` |
||||
|
||||
Descargar fixtures |
||||
------------------ |
||||
|
||||
Para descargar un fixture, ejecuta el siguiente comando: |
||||
|
||||
``` |
||||
// descarga el fixture Users, por defecto limpiará el almacenamiento del fixture (por ejemplo la tabla "users", o la colección "users" si es un fixture mongodb). |
||||
yii fixture/unload User |
||||
|
||||
// descarga varios fixtures |
||||
yii fixture/unload User,UserProfile |
||||
|
||||
// descarga todos los fixtures |
||||
yii fixture/unload "*" |
||||
|
||||
// descarga todos los fixtures excepto uno |
||||
yii fixture/unload "*" -DoNotUnloadThisOne |
||||
|
||||
``` |
||||
|
||||
Opciones de comando similares como: `namespace`, `globalFixtures` también pueden ser aplicadas a este comando. |
||||
|
||||
Configurar el Comando Globalmente |
||||
--------------------------------- |
||||
Mientras que las opciones de línea de comandos nos permiten configurar el comando de migración |
||||
en el momento, a veces queremos configurar el comando de una vez y para siempre. Por ejemplo puedes configurar |
||||
diferentes rutas de migración como a continuación: |
||||
|
||||
``` |
||||
'controllerMap' => [ |
||||
'fixture' => [ |
||||
'class' => 'yii\console\controllers\FixtureController', |
||||
'namespace' => 'myalias\some\custom\namespace', |
||||
'globalFixtures' => [ |
||||
'some\name\space\Foo', |
||||
'other\name\space\Bar' |
||||
], |
||||
], |
||||
] |
||||
``` |
||||
|
||||
Autogenerando fixtures |
||||
---------------------- |
||||
|
||||
Yii puede también autogenerar fixtures por tí basándose en algún template. Puedes generar tus fixtures con distintos datos en diferentes lenguajes y formatos. |
||||
Esta característica es realizada por la librería [Faker](https://github.com/fzaninotto/Faker) y la extensión `yii2-faker`. |
||||
Visita la [guía de la extensión](https://github.com/yiisoft/yii2-faker) para mayor documentación. |
@ -0,0 +1,11 @@
|
||||
Tests Funcionales |
||||
================= |
||||
|
||||
> Note: Esta sección se encuentra en desarrollo. |
||||
|
||||
- [Tests Funcionales de Codeception](http://codeception.com/docs/04-FunctionalTests) |
||||
|
||||
Ejecutar test funcionales de templates básicos y avanzados |
||||
---------------------------------------------------------- |
||||
|
||||
Por favor consulta las instrucciones provistas en `apps/advanced/tests/README.md` y `apps/basic/tests/README.md`. |
@ -0,0 +1,25 @@
|
||||
Tests de Unidad |
||||
=============== |
||||
|
||||
> Note: Esta sección se encuentra en desarrollo. |
||||
|
||||
Un test de unidad se encarga de verificar que una unidad simple de código funcione como se espera. En la programación orientada a objetos, |
||||
la unidad de código más básica es una clase. Por lo tanto, un test de unidad necesita verificar que cada método de la interfaz de la clase funciona apropiadamente. |
||||
Esto quiere decir que, dando diferentes parámetros de entrada, el test verifica que el método devuelve el resultado esperado. |
||||
Los tests de unidad son normalmente desarrollados por la persona que escribe las clases siendo testeadas. |
||||
|
||||
Los tests de unidad en Yii están construidos en base a PHPUnit y opcionalmente, Codeception, por lo que se recomineda consultar su respectiva documentación: |
||||
|
||||
- [Documentación de PHPUnit comienza en el capítulo 2](http://phpunit.de/manual/current/en/writing-tests-for-phpunit.html). |
||||
- [Tests de Unidad con Codeception](http://codeception.com/docs/05-UnitTests). |
||||
|
||||
Ejecutar test de unidad de templates básicos y avanzados |
||||
-------------------------------------------------------- |
||||
|
||||
Por favor consulta las instrucciones provistas en `apps/advanced/tests/README.md` y `apps/basic/tests/README.md`. |
||||
|
||||
Test de unidad del Framework |
||||
---------------------------- |
||||
|
||||
Si quieres ejecutar tests de unidad para Yii en sí, consulta |
||||
"[Comenzando a desarrollar con Yii 2](https://github.com/yiisoft/yii2/blob/master/docs/internals/getting-started.md)". |
@ -0,0 +1,231 @@
|
||||
Envío de Emails |
||||
=============== |
||||
|
||||
> Note: Esta sección se encuentra en desarrollo. |
||||
|
||||
Yii soporta composición y envío de emails. De cualquier modo, el núcleo del framework provee |
||||
sólo la funcionalidad de composición y una interfaz básica. En mecanismo de envío en sí debería |
||||
ser provisto por la extensión, dado que diferentes proyectos pueden requerir diferente implementación y esto |
||||
usualmente depende de servicios y librerías externas. |
||||
|
||||
Para la mayoría de los casos, puedes utilizar la extensión oficial [yii2-swiftmailer](https://github.com/yiisoft/yii2-swiftmailer). |
||||
|
||||
|
||||
Configuración |
||||
------------- |
||||
|
||||
La configuración del componente Mail depende de la extensión que hayas elegido. |
||||
En general, la configuración de tu aplicación debería verse así: |
||||
|
||||
```php |
||||
return [ |
||||
//.... |
||||
'components' => [ |
||||
'mailer' => [ |
||||
'class' => 'yii\swiftmailer\Mailer', |
||||
], |
||||
], |
||||
]; |
||||
``` |
||||
|
||||
|
||||
Uso Básico |
||||
---------- |
||||
|
||||
Una vez configurado el componente 'mailer', puedes utilizar el siguiente código para enviar un correo electrónico: |
||||
|
||||
```php |
||||
Yii::$app->mailer->compose() |
||||
->setFrom('from@domain.com') |
||||
->setTo('to@domain.com') |
||||
->setSubject('Asunto del mensaje') |
||||
->setTextBody('Contenido en texto plano') |
||||
->setHtmlBody('<b>Contenido HTML</b>') |
||||
->send(); |
||||
``` |
||||
|
||||
En el ejemplo anterior, el método `compose()` crea una instancia del mensaje de correo, el cual puede ser llenado y enviado. |
||||
En caso de ser necesario, puedes agregar una lógica más compleja en el proceso: |
||||
|
||||
```php |
||||
$message = Yii::$app->mailer->compose(); |
||||
if (Yii::$app->user->isGuest) { |
||||
$message->setFrom('from@domain.com') |
||||
} else { |
||||
$message->setFrom(Yii::$app->user->identity->email) |
||||
} |
||||
$message->setTo(Yii::$app->params['adminEmail']) |
||||
->setSubject('Asunto del mensaje') |
||||
->setTextBody('Contenido en texto plano') |
||||
->send(); |
||||
``` |
||||
|
||||
> Note: cada extensión 'mailer' viene en dos grandes clases: 'Mailer' y 'Message'. 'Mailer' siempre conoce |
||||
el nombre de clase especifico de 'Message'. No intentes instanciar el objeto 'Message' directamente - |
||||
siempre utiliza el método `compose()` para ello. |
||||
|
||||
Puedes también enviar varios mensajes al mismo tiempo: |
||||
|
||||
```php |
||||
$messages = []; |
||||
foreach ($users as $user) { |
||||
$messages[] = Yii::$app->mailer->compose() |
||||
// ... |
||||
->setTo($user->email); |
||||
} |
||||
Yii::$app->mailer->sendMultiple($messages); |
||||
``` |
||||
|
||||
Algunas extensiones en particular pueden beneficiarse de este enfoque, utilizando mensaje simple de red, etc. |
||||
|
||||
|
||||
Componer el contenido del mensaje |
||||
--------------------------------- |
||||
|
||||
Yii permite componer el contenido de los mensajes de correo a través de archivos de vista especiales. |
||||
Por defecto, estos archivos deben estar ubicados en la ruta '@app/mail'. |
||||
|
||||
Ejemplo de archivo de contenido de correo: |
||||
|
||||
```php |
||||
<?php |
||||
use yii\helpers\Html; |
||||
use yii\helpers\Url; |
||||
|
||||
|
||||
/* @var $this \yii\web\View instancia del componente view */ |
||||
/* @var $message \yii\mail\BaseMessage instancia del mensaje de correo recién creado */ |
||||
|
||||
?> |
||||
<h2>Este mensaje te permite visitar nuestro sitio con un sólo click</h2> |
||||
<?= Html::a('Ve a la página principal', Url::home('http')) ?> |
||||
``` |
||||
|
||||
Para componer el contenido del mensaje utilizando un archivo, simplemente pasa el nombre de la vista al método `compose()`: |
||||
|
||||
```php |
||||
Yii::$app->mailer->compose('home-link') // el resultado del renderizado de la vista se transforma en el cuerpo del mensaje aquí |
||||
->setFrom('from@domain.com') |
||||
->setTo('to@domain.com') |
||||
->setSubject('Asunto del mensaje') |
||||
->send(); |
||||
``` |
||||
|
||||
Puedes pasarle parámetros adicionales a la vista en el método `compose()`, los cuales estarán disponibles dentro de las vistas: |
||||
|
||||
```php |
||||
Yii::$app->mailer->compose('greetings', [ |
||||
'user' => Yii::$app->user->identity, |
||||
'advertisement' => $adContent, |
||||
]); |
||||
``` |
||||
|
||||
Puedes especificar diferentes archivos de vista para el contenido del mensaje en HTML y texto plano: |
||||
|
||||
```php |
||||
Yii::$app->mailer->compose([ |
||||
'html' => 'contact-html', |
||||
'text' => 'contact-text', |
||||
]); |
||||
``` |
||||
|
||||
Si especificas el nombre de la vista como un string, el resultado de su renderización será utilizado como cuerpo HTML, mientras |
||||
que el cuerpo en texto plano será compuesto removiendo todas las entidades HTML del anterior. |
||||
|
||||
El resultado de la renderización de la vista puede ser envuelta en el layout, que puede ser definido utiliazando [[yii\mail\BaseMailer::htmlLayout]] |
||||
y [[yii\mail\BaseMailer::textLayout]]. Esto funciona igual a como funcionan los layouts en una aplicación web normal. |
||||
El layout puede utilizar estilos CSS u otros contenidos compartidos: |
||||
|
||||
```php |
||||
<?php |
||||
use yii\helpers\Html; |
||||
|
||||
/* @var $this \yii\web\View instancia del componente view */ |
||||
/* @var $message \yii\mail\MessageInterface el mensaje siendo compuesto */ |
||||
/* @var $content string el resultado de la renderización de la vista principal */ |
||||
?> |
||||
<?php $this->beginPage() ?> |
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
||||
<html xmlns="http://www.w3.org/1999/xhtml"> |
||||
<head> |
||||
<meta http-equiv="Content-Type" content="text/html; charset=<?= Yii::$app->charset ?>" /> |
||||
<style type="text/css"> |
||||
.heading {...} |
||||
.list {...} |
||||
.footer {...} |
||||
</style> |
||||
<?php $this->head() ?> |
||||
</head> |
||||
<body> |
||||
<?php $this->beginBody() ?> |
||||
<?= $content ?> |
||||
<div class="footer">Saludos cordiales, el equipo de<?= Yii::$app->name ?></div> |
||||
<?php $this->endBody() ?> |
||||
</body> |
||||
</html> |
||||
<?php $this->endPage() ?> |
||||
``` |
||||
|
||||
|
||||
Adjuntar archivos |
||||
----------------- |
||||
|
||||
Puedes adjuntar archivos al mensaje utilizando los métodos `attach()` y `attachContent()`: |
||||
|
||||
```php |
||||
$message = Yii::$app->mailer->compose(); |
||||
|
||||
// Adjunta un archivo del sistema local de archivos: |
||||
$message->attach('/path/to/file.pdf'); |
||||
|
||||
// Crear adjuntos sobre la marcha |
||||
$message->attachContent('Contenido adjunto', ['fileName' => 'attach.txt', 'contentType' => 'text/plain']); |
||||
``` |
||||
|
||||
|
||||
Incrustar imágenes |
||||
------------------ |
||||
|
||||
Puedes incrustar imágenes en el mensaje utilizando el método `embed()`. Este método devuelve el id del adjunto, |
||||
que debería ser utilizado como tag 'img'. |
||||
Este método es fácil de utilizar al componer mensajes a través de un archivo de vista: |
||||
|
||||
```php |
||||
Yii::$app->mailer->compose('embed-email', ['imageFileName' => '/path/to/image.jpg']) |
||||
// ... |
||||
->send(); |
||||
``` |
||||
|
||||
Entonces, dentro de tu archivo de vista, puedes utilizar el siguiente código: |
||||
|
||||
```php |
||||
<img src="<?= $message->embed($imageFileName); ?>"> |
||||
``` |
||||
|
||||
|
||||
Testear y depurar |
||||
----------------- |
||||
|
||||
Un desarrollador a menudo necesita comprobar qué emails están siendo enviados por la aplicación, cuál es su contenido y otras cosas. |
||||
Yii concede dicha habilidad vía `yii\mail\BaseMailer::useFileTransport`. Si se habilita, esta opción hace que |
||||
los datos del mensaje sean guardados en archivos locales en vez de enviados. Esos archivos serán guardados bajo |
||||
`yii\mail\BaseMailer::fileTransportPath`, que por defecto es '@runtime/mail'. |
||||
|
||||
> Note: puedes o bien guardar los mensajes en archivos, o enviarlos a sus receptores correspondientes, pero no puedes hacer las dos cosas al mismo tiempo. |
||||
|
||||
Un archivo de mensaje puede ser abierto por un editor de texto común, de modo que puedas ver sus cabeceras, su contenido y demás. |
||||
Este mecanismo en sí puede comprobarse al depurar la aplicación o al ejecutar un test de unidad. |
||||
|
||||
> Note: el archivo de contenido de mensaje es compuesto vía `\yii\mail\MessageInterface::toString()`, por lo que depende de la extensión |
||||
actual de correo utilizada en tu aplicación. |
||||
|
||||
|
||||
Crear tu solución personalizada de correo |
||||
----------------------------------------- |
||||
|
||||
Para crear tu propia solución de correo, necesitas crear 2 clases: una para 'Mailer' y |
||||
otra para 'Message'. |
||||
Puedes utilizar `yii\mail\BaseMailer` y `yii\mail\BaseMessage` como clases base de tu solución. Estas clases |
||||
ya contienen un lógica básica, la cual se describe en esta guía. De cualquier modo, su utilización no es obligatoria, es suficiente |
||||
con implementar las interfaces `yii\mail\MailerInterface` y `yii\mail\MessageInterface`. |
||||
Luego necesitas implementar todos los métodos abstractos para construir tu solución. |
@ -0,0 +1,55 @@
|
||||
Crear tu propia estructura de Aplicación |
||||
======================================== |
||||
|
||||
> Note: Esta sección se encuentra en desarrollo. |
||||
|
||||
Mientras que los templates de proyectos [basic](https://github.com/yiisoft/yii2-app-basic) y [advanced](https://github.com/yiisoft/yii2-app-advanced) |
||||
son grandiosos para la mayoría de tus necesidades, podrías querer crear tu propio template de proyecto del cual |
||||
partir todos tus proyectos. |
||||
|
||||
Los templates de proyectos en Yii son simplemente repositorios conteniendo un archivo `composer.json`, y registrado como un paquete de Composer. |
||||
Cualquier repositorio puede ser identificado como paquete Composer, haciéndolo instalable a través del comando de Composer `create-project`. |
||||
|
||||
Dado que es un poco demasiado comenzar tu template de proyecto desde cero, es mejor utilizar uno de los |
||||
templates incorporados como una base. Utilicemos el template básico aquí. |
||||
|
||||
Clonar el Template Básico |
||||
------------------------- |
||||
|
||||
El primer paso es clonar el template básico de Yii desde su repositorio Git: |
||||
|
||||
```bash |
||||
git clone git@github.com:yiisoft/yii2-app-basic.git |
||||
``` |
||||
|
||||
Entonces espera que el repositorio sea descargado a tu computadora. Dado que los cambios realizados al template no serán enviados al repositorio, puedes eliminar el directorio `.git` |
||||
y todo su contenido de la descarga. |
||||
|
||||
Modificar los Archivos |
||||
---------------------- |
||||
|
||||
A continuación, querrás modificar el archivo `composer.json` para que refleje tu template. Cambia los valores de `name`, `description`, `keywords`, `homepage`, `license`, y `support` |
||||
de forma que describa tu nuevo template. También ajusta las opciones `require`, `require-dev`, `suggest`, y demás para que encajen con los requerimientos de tu template. |
||||
|
||||
> Note: En el archivo `composer.json`, utiliza el parámetro `writable` (bajo `extra`) para especificar |
||||
> permisos-por-archivo a ser definidos después de que la aplicación es creada a partir del template. |
||||
|
||||
Luego, pasa a modificar la estructura y contenido de la aplicación como te gustaría que sea por defecto. Finalmente, actualiza el archivo README para que sea aplicable a tu template. |
||||
|
||||
Hacer un Paquete |
||||
---------------- |
||||
|
||||
Con el template definido, crea un repositorio Git a partir de él, y sube tus archivos ahí. Si tu template va a ser de código abierto, [Github](http://github.com) es el mejor lugar para alojarlo. Si tu intención es que el template no sea colaborativo, cualquier sitio de repositorios Git servirá. |
||||
|
||||
Ahora, necesitas registrar tu paquete para Composer. Para templates públicos, el paquete debe ser registrado en [Packagist](https://packagist.org/). |
||||
Para templates privados, es un poco más complicado registrarlo. Puedes ver instrucciones para hacerlo en la [documentación de Composer](https://getcomposer.org/doc/05-repositories.md#hosting-your-own). |
||||
|
||||
Utilizar el Template |
||||
-------------------- |
||||
|
||||
Eso es todo lo que se necesita para crear un nuevo template de proyecto Yii. Ahora puedes crear tus propios proyectos a partir de este template: |
||||
|
||||
``` |
||||
composer global require "fxp/composer-asset-plugin:~1.1.1" |
||||
composer create-project --prefer-dist --stability=dev mysoft/yii2-app-coolone new-project |
||||
``` |
@ -0,0 +1,49 @@
|
||||
Usar motores de plantillas |
||||
========================== |
||||
|
||||
Por defecto, Yii utiliza PHP como su lenguaje de plantilla, pero puedes configurar Yii para que soporte otros motores de renderizado, tal como |
||||
[Twig](http://twig.sensiolabs.org/) o [Smarty](http://www.smarty.net/), disponibles como extensiones. |
||||
|
||||
El componente `view` es el responsable de renderizar las vistas. Puedes agregar un motor de plantillas personalizado reconfigurando |
||||
el comportamiento (behavior) de este componente: |
||||
|
||||
```php |
||||
[ |
||||
'components' => [ |
||||
'view' => [ |
||||
'class' => 'yii\web\View', |
||||
'renderers' => [ |
||||
'tpl' => [ |
||||
'class' => 'yii\smarty\ViewRenderer', |
||||
//'cachePath' => '@runtime/Smarty/cache', |
||||
], |
||||
'twig' => [ |
||||
'class' => 'yii\twig\ViewRenderer', |
||||
'cachePath' => '@runtime/Twig/cache', |
||||
// Array de opciones de Twig: |
||||
'options' => [ |
||||
'auto_reload' => true, |
||||
], |
||||
'globals' => ['html' => '\yii\helpers\Html'], |
||||
'uses' => ['yii\bootstrap'], |
||||
], |
||||
// ... |
||||
], |
||||
], |
||||
], |
||||
] |
||||
``` |
||||
|
||||
En el código de arriba, tanto Smarty como Twig son configurados para ser utilizables por los archivos de vista. Pero para tener ambas extensiones en tu proyecto, también necesitas modificar |
||||
tu archivo `composer.json` para incluirlos: |
||||
|
||||
``` |
||||
"yiisoft/yii2-smarty": "*", |
||||
"yiisoft/yii2-twig": "*", |
||||
``` |
||||
Ese código será agregado a la sección `require` de `composer.json`. Después de realizar ese cambio y guardar el archivo, puedes instalar estas extensiones ejecutando `composer update --prefer-dist` en la línea de comandos. |
||||
|
||||
Para más detalles acerca del uso concreto de cada motor de plantillas, visita su documentación: |
||||
|
||||
- [Guía de Twig](https://github.com/yiisoft/yii2-twig/tree/master/docs/guide) |
||||
- [Guía de Smarty](https://github.com/yiisoft/yii2-smarty/tree/master/docs/guide) |
@ -0,0 +1,68 @@
|
||||
暗号化 |
||||
====== |
||||
|
||||
個の節では、セキュリティの以下の側面について見ていきます。 |
||||
|
||||
- 乱数データの生成 |
||||
- 暗号化と複合化 |
||||
- データの完全性の確認 |
||||
|
||||
|
||||
擬似乱数データを生成する |
||||
------------------------ |
||||
|
||||
擬似乱数データはさまざまな状況で役に立ちます。 |
||||
例えば、メール経由でパスワードをリセットするときは、トークンを生成してデータベースに保存し、それをユーザにメールで送信します。 |
||||
そして、ユーザはこのトークンを自分がアカウントの所有者であることの証拠として使用します。 |
||||
このトークンがユニークかつ推測困難なものであることは非常に重要なことです。 |
||||
さもなくば、攻撃者がトークンの値を推測してユーザのパスワードをリセットする可能性があります。 |
||||
|
||||
Yii のセキュリティヘルパは擬似乱数データの生成を単純な作業にしてくれます。 |
||||
|
||||
|
||||
```php |
||||
$key = Yii::$app->getSecurity()->generateRandomString(); |
||||
``` |
||||
|
||||
暗号化と復号化 |
||||
-------------- |
||||
|
||||
Yii は秘密鍵を使ってデータを暗号化/復号化することを可能にする便利なヘルパ関数を提供しています。 |
||||
データを暗号化関数に渡して、秘密鍵を持つ者だけが復号化することが出来るようにすることが出来ます。 |
||||
例えば、何らかの情報をデータベースに保存する必要があるけれども、(たとえアプリケーションのデータベースが第三者に漏洩した場合でも) 秘密鍵を持つユーザだけがそれを見ることが出来るようにする必要がある、という場合には次のようにします。 |
||||
|
||||
```php |
||||
// $data と $secretKey はフォームから取得する |
||||
$encryptedData = Yii::$app->getSecurity()->encryptByPassword($data, $secretKey); |
||||
// $encryptedData をデータベースに保存する |
||||
``` |
||||
|
||||
そして、後でユーザがデータを読みたいときは、次のようにします。 |
||||
|
||||
```php |
||||
// $secretKey はユーザ入力から取得、$encryptedData はデータベースから取得 |
||||
$data = Yii::$app->getSecurity()->decryptByPassword($encryptedData, $secretKey); |
||||
``` |
||||
|
||||
[[\yii\base\Security::encryptByKey()]] と [[\yii\base\Security::decryptByKey()]] によって、パスワードの代わりにキーを使うことも可能です。 |
||||
|
||||
|
||||
データの完全性を確認する |
||||
------------------------ |
||||
|
||||
データが第三者によって改竄されたり、更には何らかの形で毀損されたりしていないことを確認する必要がある、という場合があります。 |
||||
Yii は二つのヘルパ関数の形で、データの完全性を確認するための簡単な方法を提供しています。 |
||||
|
||||
秘密鍵とデータから生成されたハッシュをデータにプレフィクスします。 |
||||
|
||||
```php |
||||
// $secretKey はアプリケーションまたはユーザの秘密、$genuineData は信頼できるソースから取得 |
||||
$data = Yii::$app->getSecurity()->hashData($genuineData, $secretKey); |
||||
``` |
||||
|
||||
データの完全性が毀損されていないかチェックします。 |
||||
|
||||
```php |
||||
// $secretKey はアプリケーションまたはユーザの秘密、$data は信頼できないソースから取得 |
||||
$data = Yii::$app->getSecurity()->validateData($data, $secretKey); |
||||
``` |
@ -0,0 +1,14 @@
|
||||
セキュリティ |
||||
============ |
||||
|
||||
十分なセキュリティは、すべてのアプリケーションの健全さと成功のために欠くことが出来ないものです。 |
||||
不幸なことに、理解が不足しているためか、実装の難易度が高すぎるためか、セキュリティのことになると手を抜く開発者がたくさんいます。 |
||||
Yii によって駆動されるあなたのアプリケーションを可能な限り安全にするために、Yii はいくつかの優秀な使いやすいセキュリティ機能を内蔵しています。 |
||||
|
||||
* [認証](security-authentication.md) |
||||
* [権限付与](security-authorization.md) |
||||
* [パスワードを扱う](security-passwords.md) |
||||
* [暗号化](security-cryptography.md) |
||||
* [ビューのセキュリティ](structure-views.md#security) |
||||
* [認証クライアント](https://github.com/yiisoft/yii2-authclient/blob/master/docs/guide-ja/README.md) |
||||
* [ベストプラクティス](security-best-practices.md) |
@ -0,0 +1,66 @@
|
||||
Cryptography |
||||
============ |
||||
|
||||
In this section we'll review the following security aspects: |
||||
|
||||
- Generating random data |
||||
- Encryption and Decryption |
||||
- Confirming Data Integrity |
||||
|
||||
Generating Pseudorandom Data |
||||
---------------------------- |
||||
|
||||
Pseudorandom data is useful in many situations. For example when resetting a password via email you need to generate a |
||||
token, save it to the database, and send it via email to end user which in turn will allow them to prove ownership of |
||||
that account. It is very important that this token be unique and hard to guess, else there is a possibility that attacker |
||||
can predict the token's value and reset the user's password. |
||||
|
||||
Yii security helper makes generating pseudorandom data simple: |
||||
|
||||
|
||||
```php |
||||
$key = Yii::$app->getSecurity()->generateRandomString(); |
||||
``` |
||||
|
||||
Encryption and Decryption |
||||
------------------------- |
||||
|
||||
Yii provides convenient helper functions that allow you to encrypt/decrypt data using a secret key. The data is passed through the encryption function so that only the person which has the secret key will be able to decrypt it. |
||||
For example, we need to store some information in our database but we need to make sure only the user who has the secret key can view it (even if the application database is compromised): |
||||
|
||||
|
||||
```php |
||||
// $data and $secretKey are obtained from the form |
||||
$encryptedData = Yii::$app->getSecurity()->encryptByPassword($data, $secretKey); |
||||
// store $encryptedData to database |
||||
``` |
||||
|
||||
Subsequently when user wants to read the data: |
||||
|
||||
```php |
||||
// $secretKey is obtained from user input, $encryptedData is from the database |
||||
$data = Yii::$app->getSecurity()->decryptByPassword($encryptedData, $secretKey); |
||||
``` |
||||
|
||||
It's also possible to use key instead of password via [[\yii\base\Security::encryptByKey()]] and |
||||
[[\yii\base\Security::decryptByKey()]]. |
||||
|
||||
Confirming Data Integrity |
||||
------------------------- |
||||
|
||||
There are situations in which you need to verify that your data hasn't been tampered with by a third party or even corrupted in some way. Yii provides an easy way to confirm data integrity in the form of two helper functions. |
||||
|
||||
Prefix the data with a hash generated from the secret key and data |
||||
|
||||
|
||||
```php |
||||
// $secretKey our application or user secret, $genuineData obtained from a reliable source |
||||
$data = Yii::$app->getSecurity()->hashData($genuineData, $secretKey); |
||||
``` |
||||
|
||||
Checks if the data integrity has been compromised |
||||
|
||||
```php |
||||
// $secretKey our application or user secret, $data obtained from an unreliable source |
||||
$data = Yii::$app->getSecurity()->validateData($data, $secretKey); |
||||
``` |
@ -0,0 +1,14 @@
|
||||
Security |
||||
======== |
||||
|
||||
Good security is vital to the health and success of any application. Unfortunately, many developers cut corners when it |
||||
comes to security, either due to a lack of understanding or because implementation is too much of a hurdle. To make your |
||||
Yii powered application as secure as possible, Yii has included several excellent and easy to use security features. |
||||
|
||||
* [Authentication](security-authentication.md) |
||||
* [Authorization](security-authorization.md) |
||||
* [Working with Passwords](security-passwords.md) |
||||
* [Cryptography](security-cryptography.md) |
||||
* [Views security](structure-views.md#security) |
||||
* [Auth Clients](https://github.com/yiisoft/yii2-authclient/blob/master/docs/guide/README.md) |
||||
* [Best Practices](security-best-practices.md) |