Commit 7343dbdd by Aleksandr

Merge pull request #2 from yiisoft/master

switching base
parents d4152f78 a6230b50
......@@ -15,7 +15,6 @@ env:
matrix:
fast_finish: true
allow_failures:
- php: hhvm
- php: hhvm-nightly
services:
......@@ -39,7 +38,7 @@ addons:
install:
- composer self-update && composer --version
- composer global require "fxp/composer-asset-plugin:1.0.0-beta1"
- composer global require "fxp/composer-asset-plugin:1.0.0-beta2"
- export PATH="$HOME/.composer/vendor/bin:$PATH"
# core framework:
- composer install --prefer-dist
......
......@@ -16,37 +16,36 @@ DIRECTORY STRUCTURE
```
common
config/ contains shared configurations
mail/ contains view files for e-mails
models/ contains model classes used in both backend and frontend
tests/ contains various tests for objects that are common among applications
config/ contains shared configurations
mail/ contains view files for e-mails
models/ contains model classes used in both backend and frontend
console
config/ contains console configurations
controllers/ contains console controllers (commands)
migrations/ contains database migrations
models/ contains console-specific model classes
runtime/ contains files generated during runtime
tests/ contains various tests for the console application
config/ contains console configurations
controllers/ contains console controllers (commands)
migrations/ contains database migrations
models/ contains console-specific model classes
runtime/ contains files generated during runtime
backend
assets/ contains application assets such as JavaScript and CSS
config/ contains backend configurations
controllers/ contains Web controller classes
models/ contains backend-specific model classes
runtime/ contains files generated during runtime
tests/ contains various tests for the backend application
views/ contains view files for the Web application
web/ contains the entry script and Web resources
assets/ contains application assets such as JavaScript and CSS
config/ contains backend configurations
controllers/ contains Web controller classes
models/ contains backend-specific model classes
runtime/ contains files generated during runtime
views/ contains view files for the Web application
web/ contains the entry script and Web resources
frontend
assets/ contains application assets such as JavaScript and CSS
config/ contains frontend configurations
controllers/ contains Web controller classes
models/ contains frontend-specific model classes
runtime/ contains files generated during runtime
tests/ contains various tests for the frontend application
views/ contains view files for the Web application
web/ contains the entry script and Web resources
vendor/ contains dependent 3rd-party packages
environments/ contains environment-based overrides
assets/ contains application assets such as JavaScript and CSS
config/ contains frontend configurations
controllers/ contains Web controller classes
models/ contains frontend-specific model classes
runtime/ contains files generated during runtime
views/ contains view files for the Web application
web/ contains the entry script and Web resources
widgets/ contains frontend widgets
vendor/ contains dependent 3rd-party packages
environments/ contains environment-based overrides
tests contains various tests for the advanced application
codeception/ contains tests developed with Codeception PHP Testing Framework
```
......@@ -75,7 +74,7 @@ at [getcomposer.org](http://getcomposer.org/doc/00-intro.md#installation-nix).
You can then install the application using the following command:
~~~
php composer.phar global require "fxp/composer-asset-plugin:1.0.0-beta1"
php composer.phar global require "fxp/composer-asset-plugin:1.0.0-beta2"
php composer.phar create-project --prefer-dist --stability=dev yiisoft/yii2-app-advanced advanced
~~~
......
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\bootstrap\ActiveForm;
/* @var $this yii\web\View */
/* @var $form yii\widgets\ActiveForm */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model \common\models\LoginForm */
$this->title = 'Login';
......
......@@ -95,11 +95,7 @@ class User extends ActiveRecord implements IdentityInterface
*/
public static function findByPasswordResetToken($token)
{
$expire = Yii::$app->params['user.passwordResetTokenExpire'];
$parts = explode('_', $token);
$timestamp = (int) end($parts);
if ($timestamp + $expire < time()) {
// token expired
if (!static::isPasswordResetTokenValid($token)) {
return null;
}
......@@ -110,6 +106,23 @@ class User extends ActiveRecord implements IdentityInterface
}
/**
* Finds out if password reset token is valid
*
* @param string $token password reset token
* @return boolean
*/
public static function isPasswordResetTokenValid($token)
{
if (empty($token)) {
return false;
}
$expire = Yii::$app->params['user.passwordResetTokenExpire'];
$parts = explode('_', $token);
$timestamp = (int) end($parts);
return $timestamp + $expire >= time();
}
/**
* @inheritdoc
*/
public function getId()
......
......@@ -42,7 +42,10 @@ class PasswordResetRequestForm extends Model
]);
if ($user) {
$user->generatePasswordResetToken();
if (!User::isPasswordResetTokenValid($user->password_reset_token)) {
$user->generatePasswordResetToken();
}
if ($user->save()) {
return \Yii::$app->mailer->compose('passwordResetToken', ['user' => $user])
->setFrom([\Yii::$app->params['supportEmail'] => \Yii::$app->name . ' robot'])
......
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\bootstrap\ActiveForm;
use yii\captcha\Captcha;
/* @var $this yii\web\View */
/* @var $form yii\widgets\ActiveForm */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model \frontend\models\ContactForm */
$this->title = 'Contact';
......
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\bootstrap\ActiveForm;
/* @var $this yii\web\View */
/* @var $form yii\widgets\ActiveForm */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model \common\models\LoginForm */
$this->title = 'Login';
......
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\bootstrap\ActiveForm;
/* @var $this yii\web\View */
/* @var $form yii\widgets\ActiveForm */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model \frontend\models\PasswordResetRequestForm */
$this->title = 'Request password reset';
......
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\bootstrap\ActiveForm;
/* @var $this yii\web\View */
/* @var $form yii\widgets\ActiveForm */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model \frontend\models\ResetPasswordForm */
$this->title = 'Reset password';
......
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\bootstrap\ActiveForm;
/* @var $this yii\web\View */
/* @var $form yii\widgets\ActiveForm */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model \frontend\models\SignupForm */
$this->title = 'Signup';
......
......@@ -55,7 +55,7 @@ at [getcomposer.org](http://getcomposer.org/doc/00-intro.md#installation-nix).
You can then install this application template using the following command:
~~~
php composer.phar global require "fxp/composer-asset-plugin:1.0.0-beta1"
php composer.phar global require "fxp/composer-asset-plugin:1.0.0-beta2"
php composer.phar create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic
~~~
......
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\bootstrap\ActiveForm;
use yii\captcha\Captcha;
/* @var $this yii\web\View */
/* @var $form yii\widgets\ActiveForm */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model app\models\ContactForm */
$this->title = 'Contact';
......
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use yii\bootstrap\ActiveForm;
/* @var $this yii\web\View */
/* @var $form yii\widgets\ActiveForm */
/* @var $form yii\bootstrap\ActiveForm */
/* @var $model app\models\LoginForm */
$this->title = 'Login';
......
......@@ -78,13 +78,13 @@
"yiisoft/yii2-composer": "*",
"ezyang/htmlpurifier": "4.6.*",
"cebe/markdown": "0.9.*",
"bower-asset/jquery": "2.1.*@stable | ~2.1@stable | ~1.11@stable",
"bower-asset/jquery.inputmask": "3.1.* | ~3.1",
"bower-asset/punycode": "1.3.* | ~1.3",
"bower-asset/yii2-pjax": "2.0.* | ~2.0",
"bower-asset/bootstrap": "3.2.* | ~3.1",
"bower-asset/jquery-ui": "1.11.*@stable | ~1.11@stable",
"bower-asset/typeahead.js": "0.10.* | ~0.10"
"bower-asset/jquery": "2.1.*@stable | 1.11.*@stable",
"bower-asset/jquery.inputmask": "3.1.*",
"bower-asset/punycode": "1.3.*",
"bower-asset/yii2-pjax": "2.0.*",
"bower-asset/bootstrap": "3.2.* | 3.1.*",
"bower-asset/jquery-ui": "1.11.*@stable",
"bower-asset/typeahead.js": "0.10.*"
},
"require-dev": {
"phpunit/phpunit": "3.7.*",
......
......@@ -55,9 +55,9 @@ Hay que tener en cuenta que a diferencia de [[yii\base\Widget::widget()]] que de
## Creación Widgets <a name="creating-widgets"></a>
Para crear un widget, se debe extender a [[yii\base\Widget]] y sobrescribir los métodos [[yii\base\Widget::init()]] y/o [[yii\base\Widget::run()]]. Normalmente el método 'init()' debería contener el código que estandariza las propiedades del widget, mientras que el método 'run()' debería contener el código que genere la representación resultante del widget. La representación resultante pude ser "pitada" directamente o devuelta como una cadena por el método 'run()'.
Para crear un widget, se debe extender a [[yii\base\Widget]] y sobrescribir los métodos [[yii\base\Widget::init()]] y/o [[yii\base\Widget::run()]]. Normalmente el método 'init()' debería contener el código que estandariza las propiedades del widget, mientras que el método 'run()' debería contener el código que genere la representación resultante del widget. La representación resultante puede ser "pintada" directamente o devuelta como una cadena por el método 'run()'.
En el siguiente ejemplo, 'HelloWidget' codifica en HTML y muestra el contenido asignado a su propiedad 'message'. Si la propiedad no esta establecida, mostrara "Hello World" por defecto.
En el siguiente ejemplo, 'HelloWidget' codifica en HTML y muestra el contenido asignado a su propiedad 'message'. Si la propiedad no está establecida, mostrará "Hello World" por defecto.
```php
namespace app\components;
......@@ -93,7 +93,7 @@ use app\components\HelloWidget;
<?= HelloWidget::widget(['message' => 'Good morning']) ?>
```
A se muestra una variante de 'HelloWidget' obtiene el contenido contenido entre las llamadas 'begin()' y 'end()', lo codifica en HTML y posteriormente lo muestra.
Abajo se muestra una variante de 'HelloWidget' obtiene el contenido entre las llamadas 'begin()' y 'end()', lo codifica en HTML y posteriormente lo muestra.
```php
namespace app\components;
......@@ -119,7 +119,7 @@ class HelloWidget extends Widget
Como se puede observar, el búfer de salida PHP es iniciado en 'init()' por tanto cualquier salida entre las llamadas de 'init()' y 'run()' puede ser capturada, procesada y devuelta en 'run()'.
>Info: Cuando se llama a [[yii\base\Widget::begin()]], se creara una nueva instancia del widget y el método 'init()' sera llamado al final del constructor del widget. Cuando se llama [[yii\base\Widget::end()]], el método 'run()' será llamado el resultado que devuelva sera escrito por 'end()'.
>Info: Cuando se llama a [[yii\base\Widget::begin()]], se creará una nueva instancia del widget y el método 'init()' será llamado al final del constructor del widget. Cuando se llama [[yii\base\Widget::end()]], el método 'run()' será llamado el resultado que devuelva será escrito por 'end()'.
El siguiente código muestra como usar esta nueva variante de 'HelloWidget':
......@@ -143,15 +143,15 @@ public function run()
}
```
Por defecto, las vistas para un widget deberían encontrarse en ficheros dentro del directorio 'WidgetPath/views', donde 'WidgetPath' representa el directorio que contiene el fichero de clase del widget. Por lo tanto, el anterior ejemplo representara el fichero de vista `@app/components/views/hello.php`, asumiendo que la clase del widget se encuentre en `@app/components`. Se puede sobrescribir el método [[yii\base\Widget::getViewPath()]] para personalizar el directorio que contenga los ficheros de vista del widget.
Por defecto, las vistas para un widget deberían encontrarse en ficheros dentro del directorio 'WidgetPath/views', donde 'WidgetPath' representa el directorio que contiene el fichero de clase del widget. Por lo tanto, el anterior ejemplo representará el fichero de la vista `@app/components/views/hello.php`, asumiendo que la clase del widget se encuentre en `@app/components`. Se puede sobrescribir el método [[yii\base\Widget::getViewPath()]] para personalizar el directorio que contenga los ficheros de la vista del widget.
## Mejores Practicas <a name="best-practices"></a>
## Mejores Prácticas <a name="best-practices"></a>
Los widgets son una manera orientada a objetos de reutilizar código de vistas.
Los widgets son una manera orientada a objetos de reutilizar código de las vistas.
Cuando se crean widgets, se debería continuar manteniendo el patrón MVC. En general, se debería mantener la lógica en las clases del widget y mantener la presentación en las [vistas](structure-views.md).
Los widgets deberían ser diseñados para ser autónomos. Es decir, cuando se usa un widget, se debería poder poner en una vista sin hacer nada más. Esto puede resultar complicado si un widget requiere recursos externos, tales como CSS, JavaScript, imágenes, etc. Afortunadamente Yii proporciona soporte para [paquetes asset](structure-asset-bundles.md) que pueden ser utilizados para resolver el problema.
Cuando un widget solo contiene código de vista, este es muy similar a una [vista](structure-views.md). De hecho, en este caso, su única diferencia es que un widget es una clase redistribuible, mientras que una vista es solo un script PHP llano que prefiere mantener dentro de su aplicación.
Cuando un widget sólo contiene código de vista, este es muy similar a una [vista](structure-views.md). De hecho, en este caso, su única diferencia es que un widget es una clase redistribuible, mientras que una vista es sólo un script PHP llano que prefiere mantenerse dentro de su aplicación.
Travailler avec les bases de données
======================
Cette section décrira comment créer une nouvelle page qui affiche des données pays récupérées dans une table de base
de données nommée `country`. Pour ce faire, vous allez configurer une connexion à une base de données, créer une
classe [Active Record](db-active-record.md), et définir une [action](structure-controllers.md), et créer une
[vue](structure-views.md).
Au long de ce tutoriel, vous apprendrez comment :
* Configurer une connexion à une base de données
* Définir une classe Active Record
* Requêter des données en utilisant la classe Active Record
* Afficher des données dans une vue paginée
Notez que pour finir cette section, vous aurez besoin d'avoir une connaissance basique des bases de données.
En particulier, vous devez savoir créer une base de données, et exécuter des déclarations SQL en utilisant un client de
gestion de bases de données.
Préparer la Base de Données <a name="preparing-database"></a>
--------------------
Pour commencer, créez une base de données appelée `yii2basic`, depuis laquelle vous irez chercher les données dans
votre application.
Vous pouvez créer une base de données SQLite, MySQL, PostgreSQL, MSSQL ou Oracle, car Yii gère nativement de nombreuses
applications de base de données. Par simplicité, nous supposerons que vous utilisez MySQL dans les descriptions qui
suivent.
Next, create a table named `country` in the base de données, and insert some sample data. You may run the following SQL statements to do so:
```sql
CREATE TABLE `country` (
`code` CHAR(2) NOT NULL PRIMARY KEY,
`name` CHAR(52) NOT NULL,
`population` INT(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `country` VALUES ('AU','Australia',18886000);
INSERT INTO `country` VALUES ('BR','Brazil',170115000);
INSERT INTO `country` VALUES ('CA','Canada',1147000);
INSERT INTO `country` VALUES ('CN','China',1277558000);
INSERT INTO `country` VALUES ('DE','Germany',82164700);
INSERT INTO `country` VALUES ('FR','France',59225700);
INSERT INTO `country` VALUES ('GB','United Kingdom',59623400);
INSERT INTO `country` VALUES ('IN','India',1013662000);
INSERT INTO `country` VALUES ('RU','Russia',146934000);
INSERT INTO `country` VALUES ('US','United States',278357000);
```
A ce niveau, vous avez une base de données appelée `yii2basic`, et dedans, une table `country` comportant trois colonnes, contenant dix lignes de données.
Configurer une Connexion à la BDD <a name="configuring-db-connection"></a>
---------------------------
Avant de continuer, assurons nous que vous avez installé à la fois l'extension PHP
[PDO](http://www.php.net/manual/fr/book.pdo.php) et le pilote PDO pour la base de données que vous utilisez (c'est
à dire `pdo_mysql` pour MySQL). C'est une exigence de base si votre application utilise une base de données
relationnelle.
Une fois ces éléments installés, ouvrez le fichier `config/db.php` et modifiez les paramètres pour qu'ils correspondent à votre base de données. Par défaut, le fichier contient ce qui suit :
```php
<?php
return [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=yii2basic',
'username' => 'root',
'password' => '',
'charset' => 'utf8',
];
```
Le fichier `config/db.php` est un exemple type d'outil de [configuration](concept-configurations.md) basé sur un
fichier. Ce fichier de configuration en particulier spécifie les paramètres nécessaires à la création et
l'initialisation d'une instance de [[yii\db\Connection]] grâce à laquelle vous pouvez effectuer des requêtes SQL
dans la base de données sous-jacente.
On peut accéder à connexion à la BDD configurée ci-dessus depuis le code de l'application vial'expression
`Yii::$app->db`.
> Info: Le fichier `config/db.php` sera inclus par la configuration principale de l'application `config/web.php`,
qui spécifie comment l'instante d'[application](structure-applications.md) doit être initialisée.
Pour plus d'informations, merci de vous référer à la section [Configurations](concept-configurations.md).
Créer un Active Record <a name="creating-active-record"></a>
-------------------------
Pour représenter et aller chercher des données dans la table `country`, créez une classe dérivée d'[Active Record](db-active-record.md) appelée `Country`, et enregistrez la dans le fichier `models/Country.php`.
```php
<?php
namespace app\models;
use yii\db\ActiveRecord;
class Country extends ActiveRecord
{
}
```
La classe `Country` étend [[yii\db\ActiveRecord]]. Vous n'avez pas besoin d'y écrire le moindre code ! Simplement avec
le code ci-dessus, Yii devinera le nom de la table associée au nom de la class.
> Info: Si aucune correspondance directe ne peut être faite à partir du nom de la classe, vous pouvez outrepasser la méthode [[yii\db\ActiveRecord::tableName()]] pour spécifier explicitement un nom de table.
A l'aide de la classe `Country`, vous pouvez facilement manipuler les données de la table `country`, comme dans les bribes suivantes :
```php
use app\models\Country;
// chercher toutes les lignes de la table pays et les trier par "name"
$countries = Country::find()->orderBy('name')->all();
// chercher la ligne dont la clef primaire est "US"
$country = Country::findOne('US');
// afficher "United States"
echo $country->name;
// remplace le nom du pays par "U.S.A." et le sauvegarde dans la base de données
$country->name = 'U.S.A.';
$country->save();
```
> Info: Active Record est un moyen puissant pour accéder et manipuler des données d'une base de manière orientée objet.
Vous pouvez trouver plus d'informations dans la section [Active Record](db-active-record.md). Sinon, vous pouvez
également interagir avec une base de données en utilisant une méthode de plus bas niveau d'accès aux données appelée
[Data Access Objects](db-dao.md).
Créer une Action <a name="creating-action"></a>
------------------
Pour exposer les données pays aux utilisateurs, vous devez créer une action. Plutôt que de placer la nouvelle action
dans le contrôleur `site`, comme vous l'avez fait dans les sections précédentes, il est plus cohérent de créer un
nouveau contrôleur spécifiquement pour toutes les actions liées aux données pays. Nommez ce contrôleur
`CountryController`, et créez-y une action `index`, comme suit.
```php
<?php
namespace app\controllers;
use yii\web\Controller;
use yii\data\Pagination;
use app\models\Country;
class CountryController extends Controller
{
public function actionIndex()
{
$query = Country::find();
$pagination = new Pagination([
'defaultPageSize' => 5,
'totalCount' => $query->count(),
]);
$countries = $query->orderBy('name')
->offset($pagination->offset)
->limit($pagination->limit)
->all();
return $this->render('index', [
'countries' => $countries,
'pagination' => $pagination,
]);
}
}
```
Enregistrez le code ci-dessus dans le fichier `controllers/CountryController.php`.
L'action `index` appelle `Country::find()`. Cette méthode Active Record construit une requête de BDD et récupère toutes
les données de la table `country`.
Pour limiter le nombre de pays retournés par chaque requête, la requête est paginée à l'aide d'un objet
[[yii\data\Pagination]]. L'objet `Pagination` dessert deux buts :
* Il ajuste les clauses `offset` et `limit` de la déclaration SQL représentée par la requête afin qu'elle en retourne
qu'une page de données à la fois (au plus 5 colonnes par page).
* Il est utilisé dans la vue pour afficher un pagineur qui consiste en une liste de boutons de page, comme nous
l'expliquerons dans la prochaine sous-section.
A la fin du code, l'action `index` effectue le rendu d'une vue nommée `index`, et lui transmet les données pays ainsi que les informations de pagination.
Créer une Vue <a name="creating-view"></a>
---------------
Dans le dossier `views`, commencez par créer un sous-dossier nommé `country`. Ce dossier sera utilisé pour contenir
toutes les vues rendues par le contrôleur `country`. Dans le dossier `views/country`, créez un fichier nommé
`index.php` contenant ce qui suit :
```php
<?php
use yii\helpers\Html;
use yii\widgets\LinkPager;
?>
<h1>Countries</h1>
<ul>
<?php foreach ($countries as $country): ?>
<li>
<?= Html::encode("{$country->name} ({$country->code})") ?>:
<?= $country->population ?>
</li>
<?php endforeach; ?>
</ul>
<?= LinkPager::widget(['pagination' => $pagination]) ?>
```
La vue a deux sections relatives à l'affichage des données pays. Dans la première partie, les données pays fournies
est parcourue et rendue sous forme de liste non ordonnée HTML.
Dans la deuxième partie, un widget [[yii\widgets\LinkPager]] est rendu en utilisant les informations de pagination transmises par l'action.
Le widget `LinkPager` affiche une liste de boutons de pages. Le fait de cliquer sur l'un deux rafraichit les données pays dans la page correspondante.
Essayer <a name="trying-it-out"></a>
-------------
Pour voir comment tout le code ci-dessus fonctionne, utilisez votre navigateur pour accéder à l'URL suivant :
```
http://hostname/index.php?r=country/index
```
![Liste de Pays](images/start-country-list.png)
Au début, vous verrez une page affichant cinq pays. En dessous des pays, vous verrez un pagineur avec quatre boutons.
Si vous cliquez sur le bouton "2", vous verrez la page afficher cinq autres pays de la base de données : la deuxième
page d'enregistrements.
Observez plus attentivement et vous noterez que l'URL dans le navigateur devient
```
http://hostname/index.php?r=country/index&page=2
```
En coulisse, [[yii\data\Pagination|Pagination]] fournit toutes les fonctionnalités permettant de paginer un ensemble de données :
* Au départ, [[yii\data\Pagination|Pagination]] représente la première page, qui reflète la requête SELECT de country
avec la clause `LIMIT 5 OFFSET 0`. Il en résulte que les cinq premiers pays seront trouvés et affichés.
* Le widget [[yii\widgets\LinkPager|LinkPager]] effectue le rendu des boutons de pages en utilisant les URLs créés par
[[yii\data\Pagination::createUrl()|Pagination]]. Les URLs contiendront le paramètre de requête `page`, qui représente
les différents numéros de pages.
* Si vous cliquez sur le bouton de page "2", une nouvelle requête pour la route `country/index` sera déclenchée et
traitée.
[[yii\data\Pagination|Pagination]] lit le paramètre de requête `page` dans l'URL et met le numéro de page à 2.
La nouvelle requête de pays aura donc la clause `LIMIT 5 OFFSET 5` et retournera le cinq pays suivants pour être
affichés.
Résumé <a name="summary"></a>
-------
Dans cette section, vous avez appris comment travailler avec une base de données. Vous avez également appris comment
chercher et afficher des données dans des pages avec l'aide de [[yii\data\Pagination]] et [[yii\widgets\LinkPager]].
Dans la prochaine section, vous apprendrez comment utiliser le puissant outil de génération de code, appelé
[Gii](tool-gii.md), pour vous aider à implémenter rapidement des fonctionnalités communément requises, telles que les
opérations Créer, Lire, Mettre à Jour et Supprimer (CRUD : Create-Read-Update-Delete) pour travailler avec les données
dans une table de base de données. En fait, le code que vous venez d'écrire peut être généré automatiquement dans Yii
en utilisant l'outil Gii.
Travailler avec les formulaires
==================
Cette section décrit la création d'une nouvelle page comprenant un formulaire pour recevoir des données des
utilisateurs.
La page affichera un formulaire composé d'un champ de saisie nom et un champ de saisie email.
Une fois ces deux informations reçues de l'utilisateur, la page affichera les valeurs entrées pour confirmation.
Pour atteindre ce but, vous créerez non seulement une [action](structure-controllers.md) et deux
[vues](structure-views.md), mais aussi un [modèle](structure-models.md).
Au long de ce tutoriel, vous apprendrez à :
* Créer un [modèle](structure-models.md) pour représenter les données saisies par un utilisateur à travers un
formulaire
* Déclarer des règles pour valider les données entrées
* Construire un formulaire HTML dans une [vue](structure-views.md)
Créer un Modèle <a name="creating-model"></a>
----------------
Les données demandées à l'utilisateur seront représentées par une classe de modèle `EntryForm` comme montrée ci-dessous
enregistrée dans le fichier `models/EntryForm.php`. Merci de vous référer à la section
[Auto-chargement de Classes](concept-autoloading.md) pour plus de détails sur la convention de nommage de fichiers
classes.
```php
<?php
namespace app\models;
use yii\base\Model;
class EntryForm extends Model
{
public $nom;
public $email;
public function rules()
{
return [
[['nom', 'email'], 'required'],
['email', 'email'],
];
}
}
```
La classe étend [[yii\base\Model]], une classe de base fournie par Yii, communément utilisée pour représenter des
données de formulaire.
> Info: [[yii\base\Model]] est utilisée en tant que parent pour des classes modèles qui ne sont *pas* associées à des
tables de base de données.
[[yii\db\ActiveRecord]] est normalement le parent pour les classes modèles qui correspondent à des tables de bases de
données.
La classe `EntryForm` contient deux membres publics, `nom` et `email`, qui sont utilisés pour stocker les données
saisies par l'utilisateur. Elle contient également une méthode nommée `rules()`, qui renvoie un assortiment de règles
pour valider les données. Les règles de validation déclarées ci-dessus énoncent que
* à la fois les valeurs de `nom` et `email` sont requises
* la donnée `email` doit être une adresse email syntaxiquement valide
Si vous avez un objet `EntryForm` peuplé par les données saisies par un utilisateur, vous pouvez appeler sa méthode
[[yii\base\Model::validate()|validate()]] pour déclencher les routines de validation de données. Un échec de validation
de données affectera la valeur true à la propriété [[yii\base\Model::hasErrors|hasErrors]], et vous pourrez connaître
quelles erreurs de validations sont apparues via [[yii\base\Model::getErrors|errors]].
```php
<?php
$model = new EntryForm();
$model->nom = 'Qiang';
$model->email = 'mauvais';
if ($model->validate()) {
// Bien!
} else {
// Echec!
// Use $model->getErrors()
}
```
Créer une Action <a name="creating-action"></a>
------------------
Maintenant, vous allez créer une action `entry` dans le contrôleur `site` qui utilisera le nouveau modèle. Le processus
de création et d'utilisation d'actions a été expliqué dans la section [Hello World](start-hello.md).
```php
<?php
namespace app\controllers;
use Yii;
use yii\web\Controller;
use app\models\EntryForm;
class SiteController extends Controller
{
// ...code existant...
public function actionEntry()
{
$model = new EntryForm;
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
// données valides reçues dans $model
// faire quelque chose de significatif ici avec $model ...
return $this->render('entry-confirm', ['model' => $model]);
} else {
// soit la page est affichée au début soit il y a des erreurs de validation
return $this->render('entry', ['model' => $model]);
}
}
}
```
L'action commence par créer un objet `EntryForm`. Puis, elle tente de peupler le modèle avec les données de `$_POST`,
fournies dans yii par [[yii\web\Request::post()]].
Si le modèle est peuplé avec succès (c'est à dire, si l'utilisateur a soumis le formulaire HTML), l'action appellera
[[yii\base\Model::validate()|validate()]] pour s'assurer de la validité de toutes les valeurs.
> Info: L'expression `Yii::$app` représente l'instance d'[application](structure-applications.md), qui est un singleton
accessible de manière globale. C'est aussi un [annuaire de services](concept-service-locator.md) qui fournit des
composants tels que `request`, `response`, `db`, etc. pour assister des fonctionnalités spécifiques. Dans le code
ci-dessus, le composant `request` de l'instance d'application est utilisé pour accéder aux données `$_POST`.
Si tout se passe bien, l'action effectuera le rendu d'une vue nommée `entry-confirm` pour confirmer le succès de la
soumission à l'utilisateur. Si aucune donnée n'est soumise ou si les données contiennent des erreurs, la vue `entry`
sera générée, dans laquelle le formulaire HTML sera affiché, ainsi que tout message d'erreur de validation.
> Note: Dans cet exemple très simple, nous effectuons le rendu de la page de confirmation après soumission de données
valides. En pratique, vous devriez envisager d'utiliser [[yii\web\Controller::refresh()|refresh()]] ou
[[yii\web\Controller::redirect()|redirect()]] pour éviter les
[problèmes de multiple soumission de formulaire](http://fr.wikipedia.org/wiki/Post-Redirect-Get).
Créer des Vues <a name="creating-views"></a>
--------------
Enfin, créez deux fichiers de vue nommés `entry-confirm` et `entry`. Ceux-ci seront rendus par l'action `entry`,
comme décrit précédemment.
La vue `entry-confirm` affiche simplement les données de nom et email. Elle doit être placée dans le fichier
`views/site/entry-confirm.php`.
```php
<?php
use yii\helpers\Html;
?>
<p>Vous avez entré les informations suivantes :</p>
<ul>
<li><label>Nom</label>: <?= Html::encode($model->nom) ?></li>
<li><label>Email</label>: <?= Html::encode($model->email) ?></li>
</ul>
```
La vue `entry` affiche un formulaire HTML. Elle doit être stockée dans le placée `views/site/entry.php`.
```php
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
?>
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'nom') ?>
<?= $form->field($model, 'email') ?>
<div class="form-group">
<?= Html::submitButton('Soumettre', ['class' => 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
```
La vue utilise un [widget](structure-widgets.md) puissant appelé [[yii\widgets\ActiveForm|ActiveForm]] pour construire
le formulaire HTML. Les méthodes `begin()` et `end()` du widget effectuent respectivement le rendu des tags ouvrant et
fermant du formulaire. Entre les deux appels de méthode, des champs de saisie sont créés par la méthode
[[yii\widgets\ActiveForm::field()|field()]]. Le premier champ de saisie concerne la donnée "nom", et le second la
donnée "email". Après les champs de saisie, la méthode [[yii\helpers\Html::submitButton()]] est appelée pour générer un
bouton de soumission.
Essayer <a name="trying-it-out"></a>
-------------
Pour voir comment ça fonctionne, utilisez votre navigateur pour accéder à l'URL suivante :
```
http://hostname/index.php?r=site/entry
```
Vous verrez une page affichant un formulaire comportant deux champs de saisie. Devant chaque champ de saisie, une
étiquette indique quelle donnée est attendue. Si vous cliquez sur le bouton de soumission sans entrer quoi que ce soit,
ou si vous ne fournissez pas d'adresse email valide, vous verrez un message d'erreur s'afficher à coté de chaque champ
de saisie posant problème.
![Formulaire Comportant des Erreurs de Validation](images/start-form-validation.png)
Après avoir saisi un nom et une adresse email valide et cliqué sur le bouton de soumission, vous verrez une nouvelle
page affichant les données que vous venez de saisir.
![Confirmation de la Saisie de Données](images/start-entry-confirmation.png)
### La Magie expliquée <a name="magic-explained"></a>
Vous vous demandez peut-être comment le formulaire HTML fonctionne en coulisse, parce qu'il semble presque magique
qu'il puisse afficher une étiquette pour chaque champ de saisie et afficher sans rafraichir la page des messages
d'erreur si vous n'entrez pas les données correctement.
Oui, la validation de données est initialement faite coté client en Javascript, et ensuite effectuée coté serveur en
PHP.
[[yii\widgets\ActiveForm]] est suffisamment intelligent pour extraire les règles de validation que vous avez déclarées
dans `EntryForm`, le transformer en code Javascript exécutable, et utiliser le Javascript pour effectuer la validation
des données. Dans le cas où vous auriez désactivé le Javascript sur votre navigateur, la validation sera tout de même
effectuée coté serveur, comme montré dans la méthode `actionEntry()`. Cela garantit la validité des données en toutes
circonstances.
> Attention : La validation coté client est un confort qui permet une meilleure expérience utilisateur. La validation coté serveur est toujours nécessaire, que la validation coté client soit ou non en place.
Les étiquettes des champs de saisie sont générés par la méthode `field()`, en utilisant les noms des propriété du
modèle.
Par exemple, l'étiquette `Nom` sera générée à partir de la propriété `nom`.
Vous pouvez personnaliser une étiquette dans une vue en employant le code suivant :
```php
<?= $form->field($model, 'nom')->label('Votre Nom') ?>
<?= $form->field($model, 'email')->label('Votre Email') ?>
```
> Info: Yii fournit ne nombreux widgets pour vous aider à construire rapidement des vues complexes et dynamiques.
Comme vous l'apprendrez plus tard, écrire un widget et aussi extrêmement simple. Vous voudrez sans doute transformer une grande partie de votre code de vues en widgets réutilisables pour simplifier les développements de vues futurs.
Résumé <a name="summary"></a>
-------
Dans cette section du guide, vous avez touché toutes les parties du patron de conception MVC. Vous avez appris à créer
une classe modèle pour représenter les données utilisateur et valider lesdites données.
Vous avez également appris comment recevoir des données des utilisateurs et comment les réafficher dans le navigateur.
C'est une tâche qui peut prendre beaucoup de temps lors du développement d'une application, mais Yii propose des
widgets puissants pour rendre cette tâche très facile.
Dans la prochaine section, vous apprendrez comment travailler avec des bases de données, qui sont nécessaires dans presque toutes les applications.
Générer du code avec Gii
========================
Cette section décrit comment utiliser [Gii](tool-gii.md) pour générer du code qui implémente des fonctionnalités
courrantes de sites Web automatiquement. Utiliser Gii pour auto-générer du code consiste simplement à saisir les
bonnes informations en suivant les instructions affichées sur les pages Web Gii.
Au long de ce tutoriel, vous apprendrez comment :
* Activer Gii dans votre application
* Utiliser Gii pour générer des classes Active Record
* Utiliser Gii pour générer du code implémentant les opérations CRUD pour une table de BDD
* Personnaliser le code généré par Gii
Démarrer Gii <a name="starting-gii"></a>
------------
[Gii](tool-gii.md) est fourni dans Yii en tant que [module](structure-modules.md). Vous pouvez activer Gii en le
configurant dans la propriété [[yii\base\Application::modules|modules]] de l’application. En fonction de la manière
dont vous avez créé votre application, vous trouverez peut être que le code suivant est déjà fourni dans le fichier de
configuration `config/web.php`:
```php
$config = [ ... ];
if (YII_ENV_DEV) {
$config['bootstrap'][] = 'gii';
$config['modules']['gii'] = 'yii\gii\Module';
}
```
La configuration ci-dessus établit que dans un [environnement de développement](concept-configurations.md#environment-constants),
l’application doit inclure un module appelé `gii`, qui est de classe [[yii\gii\Module]].
Si vous vérifiez le [script de démarrage](structure-entry-scripts.md) `web/index.php` de votre application, vous
les lignes suivantes, qui en gros, font que `YII_ENV_DEV` est vrai.
```php
defined('YII_ENV') or define('YII_ENV', 'dev');
```
Grâce à cette ligne, votre application est en mode développement, et aura déjà active Gii, suivant la configuration
ci-dessus. Vous pouvez maintenant accéder à Gii via l’URL suivante :
```
http://hostname/index.php?r=gii
```
> Note : Si vous accede à Gii depuis une machine autre que localhost, l’accès sera refuse par défaut pour des raisons
> de sécurité. Vous pouvez configurer Gii pour ajouter les adresses IP autorisées comme suit,
>
```php
'gii' => [
'class' => 'yii\gii\Module',
'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'] // ajustez cela suivant vos besoins
],
```
![Gii](images/start-gii.png)
Générer une Classe Active Record <a name="generating-ar"></a>
---------------------------------
Pour utiliser Gii pour générer une classe Active Record, sélectionnez le "Model Generator" (en cliquant sur le lien
dans la page index de Gii). Puis complétez le formulaire comme suit :
* Table Name: `country`
* Model Class: `Country`
![Générateur de Modèles](images/start-gii-model.png)
Ensuite, cliquez sur le bouton "Preview". Vous verrez que `models/Country.php` est listé comme fichier de classe à être créé. Vous pouvez cliquer sur le nom du fichier de classe pour pré visualiser son contenu.
Quand vous utilisez Gii, si vous aviez déjà créé le même fichier et que vous l’écraseriez, cliquez sur le bouton `diff`
à côté du nom de fichier pour voir les différences entre le code à être généré et la version existant.
![Pré Visualisation du Générateur de Modèle](images/start-gii-model-preview.png)
Quand vous écrasez un fichier existant, cochez la case située à côté de "overwrite" et ensuite, cliquez sur le bouton
"Generate". Si vous créez un nouveau fichier, il suffit de cliquer sur "Generate".
Ensuite, vous verrez une page de confirmation indiquand que le code a été généré avec succès. Si vous aviez un fichier
existant, vous verrez également un message indiquant qu’il a été écrasé par le code nouvellement généré.
Générer du Code CRUD <a name="generating-crud"></a>
--------------------
CRUD signifie Create, Read, Update, and Delete (Créer, Lire, Mettre à Jour et Supprimer), représentant le quatre tâches
communes entreprises avec des données sur la plupart des sites Web. Pour créer les fonctionnalités CRUD en utilisant
Gii, sélectionnez le "CRUD Generator" (en cliquant sur le lien dans la page index de Gii). Pour l’exemple de "country",
remplissez le formulaire résultant comme suit :
* Model Class: `app\models\Country`
* Search Model Class: `app\models\CountrySearch`
* Controller Class: `app\controllers\CountryController`
![CRUD Generator](images/start-gii-crud.png)
Ensuite, cliquez sur le bouton "Preview". Vous verrez une liste de fichiers à générer, comme ci-dessous.
![CRUD Generator Preview](images/start-gii-crud-preview.png)
Si vous aviez précédemment créé les fichiers `controllers/CountryController.php` et
`views/country/index.php` (dans la section bases de données du guide), cochez la case "overwrite" pour les remplacer.
(Les versions précédentes n’avaient pas de fonctionnalités CRUD).
Essayer <a name="trying-it-out"></a>
-------------
Pour voir comment ça fonctionne, utilisez votre navigateur pour accéder à l’URL suivant :
```
http://hostname/index.php?r=country/index
```
Vous verrez une grille de données montrant les pays de la table de la base de données. Vous pouvez trier la table, ou
lui appliquer des filtres en entrant des conditions de filtrage dans les entêtes de colonnes.
Pour chaque pays affiché dans la grille, vous pouvez choisir de visualiser les détails, le mettre à jour ou le
supprimer.
Vous pouvez aussi cliquer sur le bouton "Create Country" en haut de la grille pour que Yii vous fournisse un formulaire
permettant de créer un nouveau pays.
![Grille de Données Pays](images/start-gii-country-grid.png)
![Mettre à Jour un Pays](images/start-gii-country-update.png)
Ce qui suit est la liste des fichiers générés par Gii, au cas où vous souhaiteriez investiguer la manière dont ces
fonctionnalités sont implémentées, ou les personnaliser :
* Contrôleur: `controllers/CountryController.php`
* Modèles: `models/Country.php` et `models/CountrySearch.php`
* Vues: `views/country/*.php`
> Info: Gii est conçu pour être un outil de génération de code hautement personnalisable et extensible. L’utiliser avec
sagesse peut grandement accélérer le développement de vos applications. Pour plus de détails, merci de vous référer
à la section [Gii](tool-gii.md).
Résumé <a name="summary"></a>
-------
Dans cette section, vous avez appris à utiliser Gii pour générer le code qui implémente une fonctionnalité CRUD
complète pour les contenus stockés dans une table de base de données.
Hello World
============
Cette section decrit la méthode pour créer une nouvelle page "Hello" dans votre application.
Pour ce faire, vous allez créer une [action](structure-controllers.md#creating-actions) et une [vue](structure-views.md):
* L'application distribuera la requête à l'action
* et à son tour, l'action générera un rendu de la vue qui affiche le mot "Hello" à l'utilisateur.
A travers ce tutoriel, vous apprendrez trois choses :
1. Comment créer une [action](structure-controllers.md) pour répondre aux requêtes,
2. comment créer une [vue](structure-views.md) pour composer le contenu de la réponse, et
3. comment une application distribue les requêtes aux [actions](structure-controllers.md#creating-actions).
Créer une Action <a name="creating-action"></a>
------------------
Pour la tâche "Hello", vous allez créer une [action](structure-controllers.md#creating-actions) `dire` qui reçoit un paramètre
`message` de la requête et affiche ce message à l'utilisateur. Si la requête ne fournit pas de paramètre `message`, l'action affichera le message par défaut "Hello".
> Info: Les [actions](structure-controllers.md#creating-actions) sont les objets auxquels les utilisateurs peuvent directement se référer pour les exécuter. Les actions sont groupées par [contrôleurs](structure-controllers.md). Le résultat de l'exécution d'une action est la réponse que l'utilisateur recevra.
Les actions doivent être déclarées dans des [contrôleurs](structure-controllers.md). Par simplicité, vous pouvez déclarer l'action `dire` dans le contrôleur existant `SiteController`. Ce contrôleur est défini dans le fichier classe `controllers/SiteController.php`. Voici le début de la nouvelle action :
```php
<?php
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
// ...code existant...
public function actionDire($message = 'Hello')
{
return $this->render('dire', ['message' => $message]);
}
}
```
Dans le code ci-dessous, l'action `dire` est définie par une méthode nommée `actionDire` dans la classe `SiteController`.
Yii utilise le préfixe `action` pour faire la différence entre des méthodes actions et des méthodes non-actions dans une classe contrôleur.
Le nom suivant le préfixe `action` est associé à l'ID de l'action.
Quand vous choisissez le nom de vos actions, gardez à l'esprit comment Yii traite les IDs d'action. Les références aux IDs d'actions sont toujours effectuées en minuscules. Si un ID d'action nécessite plusieurs mots, ils seront concaténés à l'aide de tirets (par exemple `creer-commentaire`). Les noms de méthodes actions sont associés aux IDs d'actions en retirant tout tiret des IDs, en mettant la première lettre de chaque mot en majuscule, et en ajoutant un préfixe `action` au résultat. Par exemple,
l'ID d'action `creer-commentaire` correspond à l'action nommée `actionCreerCommentaire`.
La méthode action de notre exemple prend un paramètre `$message`, dont la valeur par défaut est `"Hello"` (de la même manière qu'on affecte une valeur par défaut à n'importe quel argument de fonction ou méthode en PHP). Quand l'application reçoit une requête et détermine que l'action `dire` est responsable de gérer ladite requête, l'application peuplera ce paramètre avec le paramètre du même nom trouvé dans la requête. En d'autres termes, si la requête contient un paramètre `message` ayant pour valeur `"Goodbye"`, la variable `$message` au sein de l'action recevra cette valeur.
Au sein de la méthode action, [[yii\web\Controller::render()|render()]] est appelé pour effectuer le rendu d'un fichier [vue](structure-views.md) appelé `dire`. Le paramètre `message` est également transmis à la vue afin qu'il puisse y être utilisé. Le résultat du rendu est renvoyé à l'utilisateur par la méthode action. Ce résultat sera reçu par l'application et présenté à l'utilisateur dans le navigateur (en tant qu'élément d'une page HTML complète).
Créer une Vue <a name="creating-view"></a>
---------------
Les [vues](structure-views.md) sont des scripts qu'on écrit pour générer le contenu d'une réponse.
Pour la tâche "Hello", vous allez créer une vue `dire` qui affiche le paramètre `message` reçu de la méthode action, et passé par l'action à la vue :
```php
<?php
use yii\helpers\Html;
?>
<?= Html::encode($message) ?>
```
La vue `dire` doit être enregistrée dans le fichier `views/site/dire.php`. Quand la méthode [[yii\web\Controller::render()|render()]]
est appelée dans une action, elle cherchera un fichier PHP nommé `views/ControllerID/NomDeLaVue.php`.
Notez que dans le code ci-dessus, le paramètre `message` est [[yii\helpers\Html::encode()|Encodé-HTML]]
avant d'être affiché. Cela est nécessaire car le paramètre vient de l'utilisateur, le rendant vulnérable aux [attaques cross-site scripting (XSS)](http://fr.wikipedia.org/wiki/Cross-site_scripting) en intégrant du code Javascript malicieux dans le paramètre.
Bien entendu, vous pouvez insérer plus de contenu dans la vue `dire`. Le contenu peut être des tags HTMML, du texte brut, ou même des expressions PHP.
En réalité, la vue `dire` est simplement un script PHP exécuté par la méthode [[yii\web\Controller::render()|render()]].
Le contenu affiché par le script de vue sera renvoyé à l'application en tant que résultat de réponse. L'application renverra à son tour ce résultat à l'utilisateur.
Essayer <a name="trying-it-out"></a>
-------------
Après avoir créé l'action et la vue, vous pouvez accéder à la nouvelle page en accédant à l'URL suivant :
```
http://hostname/index.php?r=site/dire&message=Hello+World
```
![Hello World](images/start-hello-world.png)
Le résultat de cet URL sera une page affichant "Hello World". La page a les mêmes entête et pied de page que les autres pages de l'application.
Si vous omettez le paramètre `message` dans l'URL, La page devrait simplement afficher "Hello". C'est parce que `message` est passé en paramètre de la méthode `actionDire()`, et quand il est omis, la valeur par défaut `"Hello"` sera employée à la place.
> Info: L nouvelle page a les mêmes entête et pied de page que les autres pages parce que la méthode [[yii\web\Controller::render()|render()]] intègrera automatiquement le résultat de la vue `dire` dans une pseudo [mise en page](structure-views.md#layouts) qui dans notre cas est située dans `views/layouts/main.php`.
Le paramètre `r` dans l'URL ci-dessus nécessite plus d'explications. Il signifie [route](runtime-routing.md), un ID unique commun toute l'application qui fait référence à une action. Le format de la route est `IDContrôleur/IDAction`. Quand l'application reçoit une requête, elle vérifie ce paramêtre, en utilisant la partie `IDContrôleur` pour déterminer quel classe contrôleur doit être instanciée pour traiter la requête. Ensuite, le contrôleur utilisera la partie `IDAction` pour déterminer quelle action doit être instanciée pour effectuer le vrait travail. Dans ce cas d'exemple, la route `site/dire`
sera comprise comme la classe contrôleur `SiteController` et l'action `dire`. Il en resultera que la méthode `SiteController::actionDire()` sera appelée pour traiter la requête.
> Info: De même que les actions, les contrôleurs ont des IDs qui les identifient de manière unique dans une application.
Les IDs de contrôleurs emploie les mêmes règles de nommage que les IDs d'actions. Les noms de classes Contrôleurs dérivent
des IDs de contrôleurs en retirant les tirets des IDs, en mettant la première lettre de chaque mot en majuscule,
et en suffixant la chaîne résultante du mot `Controller`. Par exemple, l'ID de contrôlleur `poster-commentaire` correspond
au nom de classe contrôleur `PosterCommentaireController`.
Résumé <a name="summary"></a>
-------
Dans cette section, vous avez touché aux parties contrôleur et vue du patron de conception MVC.
Vous avez créé une action au sein d'un contrôleur pour traiter une requête spécifique. Vous avez également créé une vue pour composer le contenu de la réponse. Dans ce simple exemple, aucun modèle n'a été impliqué car les seules données utilisées étaient le paramètre `message`.
Vous avez également appris ce que sont les routes dans Yii, qu'elles font office de pont entre les requêtes utilisateur et les actions des contrôleurs.
Dans la prochaine section, vous apprendrez comment créer un modèle, et ajouter une nouvelle page contenant un formulaire HTML.
Fonctionnement des applications
===============================
Après avoir installé Yii, vous obtenez une application Yii fonctionnelle accessible via l'URL `http://hostname/basic/web/index.php` ou `http://hostname/index.php`, en fonction
de votre configuration. Cette section vous initiera aux fonctionalités intégrées à l'application,
à la manière dont le code est organisé, et à la gestion des requêtes par l'application.
> Info: Par simplicité, au long de ce tutoriel de démarrage, nous supposerons que `basic/web` est la racine de votre
serveur Web, et que vous avez configuré l'URL pour accéder à votre application comme suit ou similaire :
`http://hostname/index.php`.
Pour vos besoins, merci d'ajuster les URLs dans notre description comme il convient.
Fonctionalité <a name="Functionality"></a>
-------------
L'application basique installée contient quatre pages :
* La page d'accueil, affichée quand vous accédez à l'URL `http://hostname/index.php`,
* la page "About" (A Propos),
* la page "Contact", qui présente un formulaire de contact permettant aux utilisateurs finaux de vous contacter par email,
* et la page "Login" (Connexion), qui presente un formulaire de connexion qui peut être utilisé pour authentifier des utilisateurs finaux. Essayez de vous connecter
avec "admin/admin", et vous verrez l'élément "Login" du menu principal être remplacé par "Logout" (Déconnexion).
Ces pages ont en commun une entête et un pied de page. L'entête contient une barre de menu principal qui permet la navigation
entre les différentes pages.
Vous devriez également voir une barre d'outils en bas de votre fenêtre de navigation.
C'est un [outil de déboggage](tool-debugger.md) utile fourni par Yii pour enregistrer et afficher de nombreuses informations de déboggage, telles que des messages de logs, statuts de réponses, les requêtes lancées vers la base de données, et ainsi de suite.
Structure de l'Application <a name="application-structure"></a>
---------------------
Les répertoires et fichiers les plus importants de votre application sont (en supposant que le répertoire racine de l'application est `basic`) :
```
basic/ chemin de base de l'application
composer.json utilisé par Composer, décrit les information de paquets
config/ contient les configurations de l'application et autres
console.php configuration de l'application console
web.php configuration de l'application Web
commands/ contient les classes de commandes console
controllers/ contient les classes de controlleurs
models/ contient les classes de modèles
runtime/ contient les fichiers générés par Yii au cours de l'exécution, tels que les fichiers de logs ou de cache and cache
vendor/ contient les paquets Composer installés, y compris le framework Yii
views/ contient les fichiers de vues
web/ racine Web de l'application, contient les fichiers accessibles via le Web
assets/ contient les fichiers assets (javascript et css) publiés par Yii
index.php le script de démarrage (ou bootstrap) pour l'application
yii le script d'exécution de Yii en commande console
```
Dans l'ensemble, les fichiers de l'application peuvent être séparés en deux types : ceux situés dans `basic/web` et ceux situés dans d'autres répertoires. Les premiers peuvent être atteints directement en HTTP (c'est à dire dans un navigateur), tandis que les seconds doivent pas l'être.
Yii est implémenté selon le patron de conception [modèle-vue-contrôleur (MVC)](http://fr.wikipedia.org/wiki/Mod%C3%A8le-vue-contr%C3%B4leur),
ce qui se reflète dans l'organisation des répertoires ci-dessus. Le répertoire `models` contient toutes les [classes modèles](structure-models.md),
le répertoire `views` contient tous les [scripts de vue](structure-views.md), et le répertoire `controllers` contient toutes les [classes contrôleurs](structure-controllers.md).
Le schéma suivant présente la structure statique d'une application.
![Structure Statique d'Application](images/application-structure.png)
Chaque application a un script de démarrage `web/index.php` qui est le seul script PHP de l'application accessible depuis le Web.
Le script de démarrage reçoit une requête et créé une instance d'[application](structure-applications.md) pour la traiter.
L'[application](structure-applications.md) résoud la requête avec l'aide de ses [composants](concept-components.md),
et distribue la requête aux éléments MVC. Les [Widgets](structure-widgets.md) sont utilisés dans les [vues](structure-views.md)
pour aider à créer des éléments d'interface complexes et dynamiques.
Cycle de vie d'une requête <a name="request-lifecycle"></a>
-----------------
Le diagramme suivant présente la manière dont une application traite une requête.
![Cycle de Vie d'une Requête](images/application-lifecycle.png)
1. Un utilisateur fait une requête au [script de démarrage](structure-entry-scripts.md) `web/index.php`.
2. Le script de démarrage charge la [configuration](concept-configurations.md) de l'application et créé une instance d'[application](structure-applications.md) pour traiter la requête.
3. L'application resoud la [route](runtime-routing.md) requise avec l'aide du composant d'application [requête](runtime-requests.md).
4. L'application créé une instance de [contrôleur](structure-controllers.md) pour traiter la requête.
5. Le contrôleur créé une instance d'[action](structure-controllers.md) et effectue les filtres pour l'action.
6. Si un filtre échoue, l'action est annuléee.
7. Si tous les filtres sont validés, l'action est exécutée.
8. L'action charge un modèle de donées, potentiellement depuis une base de données.
9. L'action génère une vue, lui fournissant le modèle de données.
10. Le résultat généré est renvoyé au composant d'application [réponse](runtime-responses.md).
11. Le composant réponse envoie le résultat généré au navigateur de l'utilisateur.
Предзагрузка
============
Предзагрузка это процесс настройки рабочей среды до того, как будет запущено приложение и обработан входящий запрос.
Предзагрузка осуществляется в двух местах: [во входном скрипте](structure-entry-scripts.md) и в [приложении](structure-applications.md).
Во [входном скрипте](structure-entry-scripts.md), регистрируются автозагрузчики классов различных библиотек. Этот процесс
включает в себя автозагрузчик классов Composer через `autoload.php` файл и автозагрузчик классов Yii через его `Yii` файл.
Затем входной скрипт загружает [конфигурацию](concept-configurations.md) приложения и создает объект [приложения](structure-applications.md).
В конструкторе приложения происходит следующий процесс предзагрузки:
1. Вызывается метод [[yii\base\Application::preInit()|preInit()]], которые конфигурирует свойства приложения, имеющие
наивысший приоритет, такие как [[yii\base\Application::basePath|basePath]];
2. Регистрируется [[yii\base\Application::errorHandler|обработчик ошибок]];
3. Происходит инициализация свойств приложения согласно заданной конфигурации;
4. Вызывается метод [[yii\base\Application::init()|init()]], который в свою очередь вызывает метод [[yii\base\Application::bootstrap()|bootstrap()]] для
запуска компонентов предзагрузки.
- Подключается файл манифеста `vendor/yiisoft/extensions.php`;
- Создаются и запускаются [компоненты предзагрузки](structure-extensions.md#bootstrapping-classes) объявленные в расширениях;
- Создаются и запускаются [компоненты приложения](structure-application-components.md) и/или [модули](structure-modules.md), объявленные
в свойстве [предзагрузка](structure-applications.md#bootstrap) приложения.
Поскольку предзагрузка осуществляется прежде чем будет обработан *каждый* запрос, то очень важно, чтобы этот процесс был легким и максимально оптимизированным.
Старайтесь не регистрировать слишком много компонентов в предзагрузке. Компонент предзагрузки нужен только тогда, когда он должен
участвовать в полном жизненном цикле процесса обработки запроса. Например, если модуль должен зарегистрировать дополнительные правила парсинга URL,
то он должен быть указан в свойстве [предзагрузка](structure-applications.md#bootstrap), чтобы новые правила URL были учтены при обработке запроса.
В производственном режиме включите байткод кэшеры, такие как APC, для минимизации времени необходимого на подключение и парсинг php файлов.
Некоторые большие приложения могут иметь сложную [конфигурацию](concept-configurations.md), которая разделена на несколько мелких файлов.
Если это тот самый случай, возможно вам стоит кэшировать весь конфигурационный файл и загружать его прямо из кэша до создания объекта
приложения во входном скрипте.
Интернационализация
====================
> Примечание: Этот раздел находится в разработке
Интернационализация (I18N) является частью процесса разработки приложения, которое может быть адаптировано для
нескольких языков без изменения программной логики. Это особенно важно для веб-приложений, так как потенциальные
пользователи могут приходить из разных стран.
Yii располагает несколькими средствами, призванными помочь с интернационализацией веб-приложения: [переводом
сообщений][], [форматированием чисел и дат][].
Локализация и языки
-------------------
В Yii приложении определены два языка: [[yii\base\Application::$sourceLanguage|исходный язык]] н [[yii\base\
Application::$language|язык перевода]].
На "исходном языке" написаны сообщения в коде приложения. Если мы определяем исходным языком английский, то
в коде можно использовать конструкцию:
```php
echo \Yii::t('app', 'I am a message!');
```
Язык перевода определяет, в каком виде будет отображаться текущая страница, т.е. на какой язык будут переведены
оригинальные сообщения. Этот параметр определяется в конфигурации приложения:
```php
return [
'id' => 'applicationID',
'basePath' => dirname(__DIR__),
// ...
'language' => 'ru-RU', // <- здесь!
// ...
]
```
> **Подсказка**: значение по умолчанию для [[yii\base\Application::$sourceLanguage|исходного языка]] - английский.
Вы можете установить значение текущего языка в самом приложении в соответствии с языком, который выбрал пользователь.
Это необходимо сделать до того, как будет сгенерирован какой-либо вывод, чтобы не возникло проблем с его
корректностью. Используйте простое переопределение свойства на нужное значение:
```php
\Yii::$app->language = 'ru-RU';
```
Формат для установки языка/локали: `ll-CC`, где `ll` - это двух или трёхбуквенный код языка в нижнем регистре в
соответствии со стандартом [ISO-639](http://www.loc.gov/standards/iso639-2/), а `CC` - это код страны в соответствии
со стандартом [ISO-3166](http://www.iso.org/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html).
> **Примечание**: больше информации о синтаксисе и концепции локалей можно получить в [документации проекта ICU](http://userguide.icu-project.org/locale#TOC-The-Locale-Concept).
Перевод сообщений
-----------------
Перевод используется для локализации сообщений, которые будут выведены в приложении в соответствии с языком,
который выбрал пользователь.
По сути, Yii просто находит в файле с сообщениями на выбранном языке строку, соответствующую сообщению на исходном
языке приложения. Для перевода сообщений, необходимо в самом приложении заключать их в метод [[Yii::t()]]. Первый
аргумент метода - это категория, которая позволяет группировать сообщения по определённому признаку, а второй - само
сообщение.
```php
echo \Yii::t('app', 'This is a string to translate!');
```
Yii попытается загрузить файл перевода сообщений, соответствующий текущему [[yii\base\Application::$language|языку приложения]]
из одного из источников, определённых в `i18n` [компонентах приложения](concept-components.md). Сообщения - это набор
файлов или база данных, которая содержит переведённые строки. Следующая конфигурация определяет, что сообщения
должны браться из PHP-файлов:
```php
'components' => [
// ...
'i18n' => [
'translations' => [
'app*' => [
'class' => 'yii\i18n\PhpMessageSource',
//'basePath' => '@app/messages',
//'sourceLanguage' => 'en-US',
'fileMap' => [
'app' => 'app.php',
'app/error' => 'error.php',
],
],
],
],
],
```
В приведённой конфигурации, `app*` - это шаблон, который определяет, какие категории обрабатываются источником. В нашем
случае, мы обрабатываем все, что начинается с `app`. Файлы с сообщениями находятся в `@app/messages` (папке `messages`
в вашем приложении). Массив [[yii\i18n\PhpMessageSource::fileMap|fileMap]] определяет, какой файл будет подключаться для
определённой категории. Если вы не хотите конфигурировать `fileMap`, можно положиться на соглашение, что название
категории является именем файла. Например, категория `app/error` относится к файлу `app/error.php` в рамках [[yii\i18n\PhpMessageSource::basePath|basePath]].
Переводя сообщение `\Yii::t('app', 'This is a string to translate!')` при установленном языке приложения `ru-RU`, Yii
сначала будет искать файл `@app/messages/ru-RU/app.php`, чтобы получить список доступных переводов. Если есть файл
`ru-RU`, Yii также попробует поискать `ru` перед тем, как примет решение, что попытка перевода не удалась.
Кроме хранения в PHP-файлах (используя [[yii\i18n\PhpMessageSource|PhpMessageSource]]), Yii предоставляет ещё два
класса:
- [[yii\i18n\GettextMessageSource]], использующий GNU Gettext для MO или PO файлов.
- [[yii\i18n\DbMessageSource]], использующий базу данных.
### Именованные указатели
Вы можете добавлять параметры в строку для перевода, которые в выводе будут заменены соответствующими значениями,
заключая параметр в фигурные скобки:
```php
$username = 'Alexander';
echo \Yii::t('app', 'Hello, {username}!', [
'username' => $username,
]);
```
Обратите внимание, что в операции присваивания фигурные скобки не используются.
### Позиционные указатели
```php
$sum = 42;
echo \Yii::t('app', 'Balance: {0}', $sum);
```
> **Подсказка**: старайтесь сохранять читаемость сообщений и избегать избыточного использования позиционных
параметров. Помните, что переводчик, скорее всего, будет располагать только файлом со строками и для него
должно быть очевидно, на что будет заменён тот или иной указатель.
### Указатели с расширенным форматированием
Чтобы использовать расширенные возможности, вам необходимо установить и включить [PHP-расширение intl](http://www.php.net/manual/en/intro.intl.php).
После этого вам станет доступен расширенный синтаксис указателей, а также сокращённая запись `{placeholderName, argumentType}`,
эквивалентная форме `{placeholderName, argumentType, argumentStyle}`, позволяющая определять стиль форматирования.
Полная документация доступна на [сайте ICU](http://icu-project.org/apiref/icu4c/classMessageFormat.html), но далее в
документации будут приведены примеры использования расширенных возможностей интернационализации.
#### Числа
```php
$sum = 42;
echo \Yii::t('app', 'Balance: {0, number}', $sum);
```
Вы можете использовать один из встроенных форматов (`integer`, `currency`, `percent`):
```php
$sum = 42;
echo \Yii::t('app', 'Balance: {0, number, currency}', $sum);
```
Или определить свой формат:
```php
$sum = 42;
echo \Yii::t('app', 'Balance: {0, number, ,000,000000}', $sum);
```
[Описание форматирования](http://icu-project.org/apiref/icu4c/classicu_1_1DecimalFormat.html).
#### Даты
```php
echo \Yii::t('app', 'Today is {0, date}', time());
```
Встроенные форматы - это `short`, `medium`, `long`, and `full`:
```php
echo \Yii::t('app', 'Today is {0, date, short}', time());
```
Используя свой формат:
```php
echo \Yii::t('app', 'Today is {0, date, yyyy-MM-dd}', time());
```
[Описание форматирования](http://icu-project.org/apiref/icu4c/classicu_1_1SimpleDateFormat.html).
#### Время
```php
echo \Yii::t('app', 'It is {0, time}', time());
```
Встроенные форматы - это `short`, `medium`, `long`, and `full`:
```php
echo \Yii::t('app', 'It is {0, time, short}', time());
```
Используя свой формат:
```php
echo \Yii::t('app', 'It is {0, date, HH:mm}', time());
```
[Описание форматирования](http://icu-project.org/apiref/icu4c/classicu_1_1SimpleDateFormat.html).
#### Числа прописью
```php
echo \Yii::t('app', 'Число {n,number} прописью: {n, spellout}', ['n' => 42]);
```
#### Порядковые числительные
```php
echo \Yii::t('app', 'Вы - {n, ordinal} посетитель!', ['n' => 42]);
```
Выведет сообщение "Вы - 42-й посетитель!".
#### Продолжительность
```php
echo \Yii::t('app', 'Вы находитесь здесь уже {n, duration}', ['n' => 47]);
```
Выведет сообщение "Вы находитесь здесь уже 47 сек.".
#### Множественное число
В каждом языке используется свой способ склонения порядковых числительных. Некоторые правила весьма сложны,
так что очень удобно, что использование функционала i18n не требует определения правил склонения. Требуется
только указать формы склоняемого слова в различных ситуациях:
```php
echo \Yii::t(
'app',
'На диване {n, plural, =0{нет кошек} =1{лежит одна кошка} one{лежит # кошка} few{лежит # кошки}} many{лежит # кошек}} other{лежит # кошки}}!',
['n' => 0]
);
```
Выведет сообщение "На диване нет кошек!".
В данном правиле
* `=0` означает ноль;
* `=1` соответствует ровно `1`;
* `one` - `21`, `31`, `41` и так далее;
* `few` - от `2` до `4`, от `22` до `24` и так далее;
* `many` - `0`, от `5` до `20`, от `25` до `30` и так далее;
* `other` - для всех прочих чисел (например, дробных).
* Решётка `#` заменяется на значение аргумента `n`.
Для некоторых языков правила могут быть более простыми. Например, для английского будет достаточно указать:
```php
echo \Yii::t('app', 'There {n, plural, =0{are no cats} =1{is one cat} other{are # cats}}!', ['n' => 0]);
```
Следует помнить, что если вы используете указатель дважды и в первый раз он используется, как `plural`,
второй раз он должен быть использован, как `number`, иначе вы получите ошибку "Inconsistent types declared
for an argument: U_ARGUMENT_TYPE_MISMATCH":
```
В корзине: {count, number} {count, plural, one{товар} few{товара} other{товаров}}.
```
Подробная документация о формах склонений для различных языков доступна на сайте
[unicode.org](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html).
#### Вариации
Вы можете указывать критерии форматирования сообщений в зависимости от ключевых слов. Приведённый пример
демонстрирует возможность подстановки корректного рода в зависимости от параметра:
```php
echo \Yii::t('app', '{name} - {gender} и {gender, select, женщина{ей} мужчина{ему} other{ему} нравится Yii!', [
'name' => 'Василий',
'gender' => 'мужчина',
]);
```
Выведет сообщение "Василий - мужчина и ему нравится Yii!".
Вы приведённом выражении, `мужчина` и `женщина` - это возможные варианты пола. На всякий случай, `other`
обработает случай, если значение не совпадает с первыми двумя вариантами. Строки в скобках являются вторичными
выражениями и могут быть просто строкой или строкой, содержащей дополнительные указатели.
### Определение перевода по умолчанию
Вы можете определить переводы, которые будут использованы, как переводы по умолчанию для категорий, которые
не попадают в другие переводы. Этот перевод должен быть помечен звёздочкой `*` и указан в конфигурации
приложения, как:
```php
// конфигурация i18n компонента
'i18n' => [
'translations' => [
'*' => [
'class' => 'yii\i18n\PhpMessageSource'
],
],
],
```
Теперь можно использовать категории без необходимости конфигурировать каждую из них, что похоже на
способ, которым была реализована поддержка интернационализации в Yii 1.1. Сообщения для категории будут
загружаться из файла с переводом по умолчанию из `basePath`, т.е. `@app/messages`:
```php
echo Yii::t('not_specified_category', 'message from unspecified category');
```
Сообщение будет загружено из файла `@app/messages/<LanguageCode>/not_specified_category.php`
### Перевод сообщений модулей
Если вы хотите перевести сообщения в модуле и при этом не сгружать их все в один файл, можете прибегнуть
к следующему приёму:
```php
<?php
namespace app\modules\users;
use Yii;
class Module extends \yii\base\Module
{
public $controllerNamespace = 'app\modules\users\controllers';
public function init()
{
parent::init();
$this->registerTranslations();
}
public function registerTranslations()
{
Yii::$app->i18n->translations['modules/users/*'] = [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en-US',
'basePath' => '@app/modules/users/messages',
'fileMap' => [
'modules/users/validation' => 'validation.php',
'modules/users/form' => 'form.php',
...
],
];
}
public static function t($category, $message, $params = [], $language = null)
{
return Yii::t('modules/users/' . $category, $message, $params, $language);
}
}
```
В приведённом примере мы использовали маску для поиска совпадений, и последующую фильтрацию по
категориям для искомого файла. Вместо использования `fileMap`, вы можете прибегнуть к соглашению,
что имя категории совпадает с именем файла и писать `Module::t('validation', 'your custom validation message')` или
`Module::t('form', 'some form label')` напрямую.
### Перевод сообщений виджетов
Для виджетов применимо такое же правило, как и для модулей:
```php
<?php
namespace app\widgets\menu;
use yii\base\Widget;
use Yii;
class Menu extends Widget
{
public function init()
{
parent::init();
$this->registerTranslations();
}
public function registerTranslations()
{
$i18n = Yii::$app->i18n;
$i18n->translations['widgets/menu/*'] = [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en-US',
'basePath' => '@app/widgets/menu/messages',
'fileMap' => [
'widgets/menu/messages' => 'messages.php',
],
];
}
public function run()
{
echo $this->render('index');
}
public static function t($category, $message, $params = [], $language = null)
{
return Yii::t('widgets/menu/' . $category, $message, $params, $language);
}
}
```
Вместо использования `fileMap`, вы можете прибегнуть к соглашению, что имя категории совпадает с
именем файла и писать `Menu::t('messages', 'new messages {messages}', ['{messages}' => 10])` напрямую.
> **Примечание**: для виджетов вы можете использовать i18n представления. На них распространяются
> те же правила, что и на контроллеры.
### Перевод сообщений фреймворка
Yii поставляется с набором сообщений по умолчанию для ошибок валидации и некоторых других строк. Эти
сообщения принадлежат категории `yii`. Если возникает необходимость изменить сообщения по умолчанию,
переопределите `i18n` [компонент приложения](concept-components.md):
```php
'i18n' => [
'translations' => [
'yii' => [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en-US',
'basePath' => '@app/messages'
],
],
],
```
После этого разместите изменённые строки в файле `@app/messages/<language>/yii.php`.
### Обработка недостающих переводов
Если в источнике перевода отсутствует необходимое сообщение, Yii отобразит исходное содержимое сообщения.
Данное поведение тем оправданнее, чем вы более стремитесь писать в исходном коде понятный текст сообщений.
Тем не менее, иногда этого недостаточно, и может потребоваться произвольная обработка возникшей ситуации,
когда источник не содержит искомой строки. Для этого следует использовать обработку события
[[yii\i18n\MessageSource::EVENT_MISSING_TRANSLATION|missingTranslation]] компонента [[yii\i18n\MessageSource]].
Например, чтобы отметить все непереведённые строки, чтобы их было легче находить на странице, необходимо
создать обработчик события. Изменим конфигурацию приложения:
```php
'components' => [
// ...
'i18n' => [
'translations' => [
'app*' => [
'class' => 'yii\i18n\PhpMessageSource',
'fileMap' => [
'app' => 'app.php',
'app/error' => 'error.php',
],
'on missingTranslation' => ['app\components\TranslationEventHandler', 'handleMissingTranslation']
],
],
],
],
```
Создадим обработчик события:
```php
<?php
namespace app\components;
use yii\i18n\MissingTranslationEvent;
class TranslationEventHandler
{
public static function(MissingTranslationEvent $event) {
$event->translatedMessage = "@MISSING: {$event->category}.{$event->message} FOR LANGUAGE {$event->language} @";
}
}
```
Если [[yii\i18n\MissingTranslationEvent::translatedMessage]] установлен, как обработчик события, на странице будет
выведен соответствующий результат перевода.
> Внимание: каждый источник обрабатывает недостающие переводы самостоятельно. Если вы используете несколько разных
> источников сообщений и хотите обрабатывать недостающие переводы одинаково для всех, назначьте соответствующий
> обработчик события для каждого источника.
Представления
-------------
Вместо того, чтобы переводить сообщения так, как указано в предыдущем разделе, вы можете использовать `i18n` в ваших
представлениях, чтобы обеспечить поддержку нескольких языков. Например, если существует представление `views/site/index.php`
и для перевода его на русский язык необходимо отдельное представление, создайте папку `ru-RU` в папке с представлением
текущего контроллера или виджета и создайте файл для русского языка: `views/site/ru-RU/index.php`. Yii загрузит файл
для текущего языка, если он существует, или использует исходный `views/site/index.php`, если не сможет найти локализацию.
> **Примечание**: если язык был определён, как `en-US` и соответствующих представлений не было найдено, Yii попробует
> найти представления в папке `en` перед тем, как использовать исходные.
Форматирование чисел и дат
--------------------------
См. описание [форматирования дат](output-formatter.md).
Настройка PHP-окружения <a name="setup-environment"></a>
--------------------------------------------------------
Для работы с большей частью функций интернационализации, Yii использует [PHP-расширение intl](http://php.net/manual/en/book.intl.php).
Например, это расширение используют классы, отвечающие за форматирование чисел и дат [[yii\i18n\Formatter]] и за форматирование
строк [[yii\i18n\MessageFormatter]]. Оба класса поддерживают базовый функционал даже в том случае, если расширение `intl` не
установлено. Однако, этот запасной вариант более-менее будет работать только для сайтов на английском языке, хотя, даже для
них, большая часть широких возможностей расширения `intl` не будет доступна, поэтому его установка настоятельно рекомендуется.
[PHP-расширение intl](http://php.net/manual/en/book.intl.php) основано на [библиотеке ICU](http://site.icu-project.org/), которая
описывает правила форматирования для различных локалей. Поэтому следует помнить, что форматирование чисел и дат вместе с
синтаксисом форматирования может отличаться в зависимости от версии библиотеки ICU, которая была скомпилирована в вашем
дистрибутиве PHP.
Чтобы сайт работал одинаково во всех окружениях, рекомендуется устанавливать одинаковую версию расширения intl, при этом
удостоверяясь, что везде используется одинаковая версия библиотеки ICU.
Чтобы узнать, какая версия ICU используется текущим PHP интерпретатором, используйте следующий скрипт:
```
<?php
echo "PHP: " . PHP_VERSION . "\n";
echo "ICU: " . INTL_ICU_VERSION . "\n";
```
Чтобы иметь доступ ко всем возможностям, описанным в документации, мы рекомендуем использовать ICU версии 49 или новее.
В более ранних версиях отсутствует указатель `#` в правилах склонений. На сайте <http://site.icu-project.org/download>
вы можете ознакомиться со списком доступных версий ICU. Обратите внимание, что схема нумерации версий изменилась после
версии 4.8 и последовательность версий выглядит так: ICU 4.8, ICU 49, ICU 50.
......@@ -20,7 +20,7 @@
Composer 安装后,切换到一个可通过 Web 访问的目录,执行如下命令即可安装 Yii :
composer global require "fxp/composer-asset-plugin:1.0.0-beta1"
composer global require "fxp/composer-asset-plugin:1.0.0-beta2"
composer create-project --prefer-dist yiisoft/yii2-app-basic basic
第一条命令安装 [composer asset plugin](https://github.com/francoispluchino/composer-asset-plugin/),它是通过 Composer 管理 bower 和 npm 包所必须的,此命令全局应用,只需执行一次即可。
......
......@@ -47,10 +47,11 @@ Application Structure
Handling Requests
-----------------
* **TBD** [Bootstrapping](runtime-bootstrapping.md)
* **TBD** [Routing](runtime-routing.md)
* **TBD** [Requests](runtime-requests.md)
* **TBD** [Responses](runtime-responses.md)
* [Overview](runtime-overview.md)
* [Bootstrapping](runtime-bootstrapping.md)
* [Routing](runtime-routing.md)
* [Requests](runtime-requests.md)
* [Responses](runtime-responses.md)
* **TBD** [Sessions and Cookies](runtime-sessions-cookies.md)
* [URL Parsing and Generation](runtime-url-handling.md)
* [Handling Errors](runtime-handling-errors.md)
......@@ -81,7 +82,7 @@ Working with Databases
* **TBD** [Sphinx](db-sphinx.md)
* **TBD** [Redis](db-redis.md)
* **TBD** [MongoDB](db-mongodb.md)
* **TBD** [ElasticSearch](db-elastic-search.md)
* **TBD** [ElasticSearch](db-elasticsearch.md)
Getting Data from Users
......@@ -96,7 +97,7 @@ Getting Data from Users
Displaying Data
---------------
* **TBD** [Data Formatting](output-formatting.md)
* **TBD** [Data Formatting](output-formatter.md)
* **TBD** [Pagination](output-pagination.md)
* **TBD** [Sorting](output-sorting.md)
* [Data Providers](output-data-providers.md)
......@@ -151,7 +152,7 @@ Testing
-------
* [Overview](test-overview.md)
* [Testing environment setup](test-endvironment-setup.md)
* [Testing environment setup](test-environment-setup.md)
* [Unit Tests](test-unit.md)
* [Functional Tests](test-functional.md)
* [Acceptance Tests](test-acceptance.md)
......
......@@ -177,7 +177,7 @@ function whose return value determines whether to apply the rule or not. For exa
['state', 'required', 'when' => function ($model) {
return $model->country == 'USA';
}, 'whenClient' => "function (attribute, value) {
return $('#country').value == 'USA';
return $('#country').val() == 'USA';
}"],
]
```
......
......@@ -68,7 +68,8 @@ For more details about the available properties check out the [[yii\i18n\Formatt
'decimalSeparator' => ',',
'thousandSeparator' => ' ',
'currencyCode' => 'EUR',
];
],
],
```
Formatting Dates
......@@ -88,6 +89,8 @@ See http://site.icu-project.org/ for the format.
and now in human readable form.
The input value for date and time formatting is assumed to be in UTC unless a timezone is explicitly given.
Formatting Numbers
------------------
......
......@@ -17,7 +17,7 @@ with response formatting:
Yii supports content negotiation via the [[yii\filters\ContentNegotiator]] filter. The RESTful API base
controller class [[yii\rest\Controller]] is equipped with this filter under the name of `contentNegotiator`.
The filer provides response format negotiation as well as language negotiation. For example, if a RESTful
The filter provides response format negotiation as well as language negotiation. For example, if a RESTful
API request contains the following header,
```
......
......@@ -75,4 +75,4 @@ For example, to support a new action `search` by the endpoint `GET /users/search
You may have noticed that the controller ID `user` appears in plural form as `users` in the endpoints.
This is because [[yii\rest\UrlRule]] automatically pluralizes controller IDs for them to use in endpoints.
You may disable this behavior by setting [[yii\rest\UrlRule::pluralize]] to be false, or if you want
to use some special names you may configure the [[yii\rest\UrlRule::controller]] property.
to use some special names you may configure the [[yii\rest\UrlRule::controller]] property. Note that the pluralization of RESTful endpoints does not always simply add an "s" to the end of the controller id. A controller whose ID ends in "x", for example "BoxController" (with ID `box`), has RESTful endpoints pluralized to `boxes` by [[yii\rest\UrlRule]].
......@@ -69,9 +69,11 @@ return [
'modules' => [
'v1' => [
'basePath' => '@app/modules/v1',
'controllerNamespace' => 'app\modules\v1\controllers',
],
'v2' => [
'basePath' => '@app/modules/v2',
'controllerNamespace' => 'app\modules\v2\controllers',
],
],
'components' => [
......
Bootstrapping
=============
Bootstrapping refers to the process of preparing the environment before an application starts
to resolve and process an incoming request. Bootstrapping is done in two places:
the [entry script](structure-entry-scripts.md) and the [application](structure-applications.md).
In the [entry script](structure-entry-scripts.md), class autoloaders for different libraries are
registered. This includes the Composer autoloader through its `autoload.php` file and the Yii
autoloader through its `Yii` class file. The entry script then loads the application
[configuration](concept-configurations.md) and creates an [application](structure-applications.md) instance.
In the constructor of the application, the following bootstrapping work are done:
1. [[yii\base\Application::preInit()|preInit()]] is called, which configures some high priority
application properties, such as [[yii\base\Application::basePath|basePath]].
2. Register the [[yii\base\Application::errorHandler|error handler]].
3. Initialize application properties using the given application configuration.
4. [[yii\base\Application::init()|init()]] is called which in turn calls
[[yii\base\Application::bootstrap()|bootstrap()]] to run bootstrapping components.
- Include the extension manifest file `vendor/yiisoft/extensions.php`.
- Create and run [bootstrap components](structure-extensions.md#bootstrapping-classes)
declared by extensions.
- Create and run [application components](structure-application-components.md) and/or
[modules](structure-modules.md) that are declared in the application's
[bootstrap property](structure-applications.md#bootstrap).
Because the bootstrapping work has to be done before handling *every* request, it is very important
to keep this process light and optimize it as much as possible.
Try not to register too many bootstrapping components. A bootstrapping component is needed only
if it wants to participate the whole life cycle of requesting handling. For example, if a module
needs to register additional URL parsing rules, it should be listed in the
[bootstrap property](structure-applications.md#bootstrap) so that the new URL rules can take effect
before they are used to resolve requests.
In production mode, enable bytecode cache, such as APC, to minimize the time needed for including
and parsing PHP files.
Some large applications have very complex application [configurations](concept-configurations.md)
which are divided into many smaller configuration files. If this is the case, consider caching
the whole configuration array and loading it directly from cache before creating the application instance
in the entry script.
Overview
========
Each time when a Yii application handles a request, it undergoes a similar workflow.
1. A user makes a request to the [entry script](structure-entry-scripts.md) `web/index.php`.
2. The entry script loads the application [configuration](concept-configurations.md) and creates
an [application](structure-applications.md) instance to handle the request.
3. The application resolves the requested [route](runtime-routing.md) with the help of
the [request](runtime-requests.md) application component.
4. The application creates a [controller](structure-controllers.md) instance to handle the request.
5. The controller creates an [action](structure-controllers.md) instance and performs the filters for the action.
6. If any filter fails, the action is cancelled.
7. If all filters pass, the action is executed.
8. The action loads a data model, possibly from a database.
9. The action renders a view, providing it with the data model.
10. The rendered result is returned to the [response](runtime-responses.md) application component.
11. The response component sends the rendered result to the user's browser.
The following diagram shows how an application handles a request.
![Request Lifecycle](images/application-lifecycle.png)
In this section, we will describe in detail how some of these steps work.
Requests
========
Requests made to an application are represented in terms of [[yii\web\Request]] objects which provide information
such as request parameters, HTTP headers, cookies, etc. For a given request, you can get access to the corresponding
request object via the `request` [application component](structure-application-components.md). In this section,
we will describe how you can make use of this component in your applications.
## Request Parameters <a name="request-parameters"></a>
To get request parameters, you can call [[yii\web\Request::get()|get()]] and [[yii\web\Request::post()|post()]] methods
of the `request` component. They return the values of `$_GET` and `$_POST`, respectively. For example,
```php
$request = Yii::$app->request;
$get = $request->get();
// equivalent to: $get = $_GET;
$id = $request->get('id');
// equivalent to: $id = isset($_GET['id']) ? $_GET['id'] : null;
$id = $request->get('id', 1);
// equivalent to: $id = isset($_GET['id']) ? $_GET['id'] : 1;
$post = $request->post();
// equivalent to: $post = $_POST;
$name = $request->post('name');
// equivalent to: $name = isset($_POST['name']) ? $_POST['name'] : null;
$name = $request->post('name', '');
// equivalent to: $name = isset($_POST['name']) ? $_POST['name'] : '';
```
> Info: Instead of directly accessing `$_GET` and `$_POST` to retrieve the request parameters, it is recommended
that you get them via the `request` component like shown above. This will make writing tests easier because
you can create a mock request component with faked request data.
When implementing [RESTful APIs](rest-quick-start.md), you often need to retrieve parameters that are submitted
via PUT, PATCH or other [request methods](#request-methods). You can get these parameters by calling
the [[yii\web\Request::getBodyParam()]] methods. For example,
```php
$request = Yii::$app->request;
// returns all parameters
$params = $request->bodyParams;
// returns the parameter "id"
$param = $request->getBodyParam('id');
```
> Info: Unlike `GET` parameters, parameters submitted via `POST`, `PUT`, `PATCH` etc. are sent in the request body.
The `request` component will parse these parameters when you access them through the methods described above.
You can customize the way how these parameters are parsed by configuring the [[yii\web\Request::parsers]] property.
## Request Methods <a name="request-methods"></a>
You can get the HTTP method used by the current request via the expression `Yii::$app->request->method`.
A whole set of boolean properties are also provided for you to check if the current method is of certain type.
For example,
```php
$request = Yii::$app->request;
if ($request->isAjax) { // the request is an AJAX request }
if ($request->isGet) { // the request method is GET }
if ($request->isPost) { // the request method is POST }
if ($request->isPut) { // the request method is PUT }
```
## Request URLs <a name="request-urls"></a>
The `request` component provides many ways of inspecting the currently requested URL.
Assuming the URL being requested is `http://example.com/admin/index.php/product?id=100`, you can get various
parts of this URL as summarized in the following:
* [[yii\web\Request::url|url]]: returns `/admin/index.php/product?id=100`, which is the URL without the host info part.
* [[yii\web\Request::absoluteUrl|absoluteUrl]]: returns `http://example.com/admin/index.php/product?id=100`,
which is the whole URL including the host info part.
* [[yii\web\Request::hostInfo|hostInfo]]: returns `http://example.com`, which is the host info part of the URL.
* [[yii\web\Request::pathInfo|pathInfo]]: returns `/product`, which is the part after the entry script and
before the question mark (query string).
* [[yii\web\Request::queryString|queryString]]: returns `id=100`, which is the part after the question mark.
* [[yii\web\Request::baseUrl|baseUrl]]: returns `/admin`, which is the part after the host info and before
the entry script name.
* [[yii\web\Request::scriptUrl|scriptUrl]]: returns `/admin/index.php`, which is the URL without path info and query string.
* [[yii\web\Request::serverName|serverName]]: returns `example.com`, which is the host name in the URL.
* [[yii\web\Request::serverPort|serverPort]]: returns 80, which is the port used by the Web server.
## HTTP Headers <a name="http-headers"></a>
You can get the HTTP header information through the [[yii\web\HeaderCollection|header collection]] returned
by the [[yii\web\Request::headers]] property. For example,
```php
// $headers is an object of yii\web\HeaderCollection
$headers = Yii::$app->request->headers;
// returns the Accept header value
$accept = $headers->get('Accept');
if ($headers->has('User-Agent')) { // there is User-Agent header }
```
The `request` component also provides support for quickly accessing some commonly used headers, including
* [[yii\web\Request::userAgent|userAgent]]: returns the value of the `User-Agent` header.
* [[yii\web\Request::contentType|contentType]]: returns the value of the `Content-Type` header which indicates
the MIME type of the data in the request body.
* [[yii\web\Request::acceptableContentTypes|acceptableContentTypes]]: returns the content MIME types acceptable by users.
The returned types ordered by the quality score. Types with the highest scores will be returned first.
* [[yii\web\Request::acceptableLanguages|acceptableLanguages]]: returns the languages acceptable by users.
The returned languages are ordered by their preference level. The first element represents the most preferred language.
If your application supports multiple languages and you want to display pages in the language that is the most preferred
by the end user, you may use the language negotiation method [[yii\web\Request::getPreferredLanguage()]].
This method takes a list of languages supported by your application, compares them with [[yii\web\Request::acceptableLanguages|acceptableLanguages]],
and returns the most appropriate language.
> Tip: You may also use the [[yii\filters\ContentNegotiator|ContentNegotiator]] filter to dynamically determine
what content type and language should be used in the response. The filter implements the content negotiation
on top the properties and methods described above.
## Client Information <a name="client-information"></a>
You can get the host name and IP address of the client machine through [[yii\web\Request::userHost|userHost]]
and [[yii\web\Request::userIP|userIP]], respectively. For example,
```php
$userHost = Yii::$app->request->userHost;
$userIP = Yii::$app->request->userIP;
```
Responses
=========
When an application finishes handling a [request](runtime-requests.md), it generates a [[yii\web\Response|response]] object
and sends it to the end user. The response object contains information such as the HTTP status code, HTTP headers and body.
The ultimate goal of Web application development is essentially to build such response objects upon various requests.
In most cases you should mainly deal with the `response` [application component](structure-application-components.md).
However, Yii also allows you to create your own response objects and send them to end users.
In this section, we will describe how to compose and send responses to end users.
## Status Code <a name="status-code"></a>
One of the first things you would do when building a response is to state whether the request is successfully handled.
This is done by setting the [[yii\web\Response::statusCode]] property which can take one of the valid
[HTTP status codes](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html). For example, to indicate the request
is successfully handled, you may set the status code to be 200, like the following:
```php
Yii::$app->response->statusCode = 200;
```
However, in most cases you do not need to explicitly set the status code. This is because the default value
of [[yii\web\Response::statusCode]] is 200. And if you want to indicate the request is unsuccessful, you may
throw an appropriate HTTP exception like the following:
```php
throw new \yii\web\NotFoundHttpException;
```
When the [error handler](runtime-handling-errors.md) catches an exception, it will extract the status code
from the exception and assign it to the response. For the [[yii\web\NotFoundHttpException]] above, it is
associated with the HTTP status 404. The following HTTP exceptions are predefined in Yii:
* [[yii\web\BadRequestHttpException]]: status code 400.
* [[yii\web\ConflictHttpException]]: status code 409.
* [[yii\web\ForbiddenHttpException]]: status code 403.
* [[yii\web\GoneHttpException]]: status code 410.
* [[yii\web\MethodNotAllowedHttpException]]: status code 405.
* [[yii\web\NotAcceptableHttpException]]: status code 406.
* [[yii\web\NotFoundHttpException]]: status code 404.
* [[yii\web\ServerErrorHttpException]]: status code 500.
* [[yii\web\TooManyRequestsHttpException]]: status code 429.
* [[yii\web\UnauthorizedHttpException]]: status code 401.
* [[yii\web\UnsupportedMediaTypeHttpException]]: status code 415.
If the exception that you want to throw is not among the above list, you may create one by extending
from [[yii\web\HttpException]], or directly throw it with a status code, for example,
```php
throw new \yii\web\HttpException(402);
```
## HTTP Headers <a name="http-headers"></a>
You can send HTTP headers by manipulating the [[yii\web\Response::headers|header collection]] in the `response` component.
For example,
```php
$headers = Yii::$app->response->headers;
// add a Pragma header. Existing Pragma headers will NOT be overwritten.
$headers->add('Pragma', 'no-cache');
// set a Pragma header. Any existing Pragma headers will be discarded.
$headers->add('Pragma', 'no-cache');
// remove Pragma header(s) and return the removed Pragma header values in array
$values = $headers->remove('Pragma');
```
> Info: Header names are case insensitive. And the newly registered headers are not sent to the user until
the [[yii\web\Response::send()]] method is called.
## Response Body <a name="response-body"></a>
Most responses should have a body which gives the content that you want to show to end users.
If you already have a formatted body string, you may assign it to the [[yii\web\Response::content]] property
of the response. For example,
```php
Yii::$app->request->content = 'hello world!';
```
If you data needs to be formatted before sending to end users, you should set both of the
[[yii\web\Response::format|format]] and [[yii\web\Response::data|data]] properties. The [[yii\web\Response::format|format]]
property specifies in which format should the [[yii\web\Response::data|data]] be formatted as. For example,
```php
$response = Yii::$app->request;
$response->format = \yii\web\Response::FORMAT_JSON;
$response->data = ['message' => 'hello world'];
```
Yii supports the following formats out of box, each implemented by a [[yii\web\ResponseFormatterInterface|formatter]] class.
You can customize these formatters or add new ones by configuring the [[yii\web\Response::formatters]] property.
* [[yii\web\Response::FORMAT_HTML|HTML]]: implemented by [[yii\web\HtmlResponseFormatter]].
* [[yii\web\Response::FORMAT_XML|XML]]: implemented by [[yii\web\XmlResponseFormatter]].
* [[yii\web\Response::FORMAT_JSON|JSON]]: implemented by [[yii\web\JsonResponseFormatter]].
* [[yii\web\Response::FORMAT_JSONP|JSONP]]: implemented by [[yii\web\JsonResponseFormatter]].
While response body can be set explicitly as shown above, in most cases you may set it implicitly by the return value
of [action](structure-controllers.md) methods. A common use case is like the following:
```php
public function actionIndex()
{
return $this->render('index');
}
```
The `index` action above returns the rendering result of the `index` view. The return value will be taken
by the `response` component, formatted and then sent to end users.
Because by default, the response format is as [[yii\web\Response::FORMAT_HTML|HTML]], you should only return a string
in an action method. If you want to use a different response format, you should set it first before returning the data.
For example,
```php
public function actionInfo()
{
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
return [
'message' => 'hello world',
'code' => 100,
];
}
```
As aforementioned, besides using the default `response` application component, you can also create your own
response objects and send them to end users. You can do so by returning such an object in an action method, like the following,
```php
public function actionInfo()
{
return \Yii::createObject([
'class' => 'yii\web\Response',
'format' => \yii\web\Response::FORMAT_JSON,
'data' => [
'message' => 'hello world',
'code' => 100,
],
]);
}
```
> Note: If you are creating your own response objects, you will not be able to take advantage of the configurations
that you set for the `response` component in the application configuration. You can, however, use
[dependency injection](concept-di-container.md) to apply common configuration to your new response objects.
## Browser Redirection <a name="browser-redirection"></a>
Browser redirection relies on sending a `Location` HTTP header. Because this feature is commonly used, Yii provides
some special supports for it.
You can redirect the user browser to a URL by calling the [[yii\web\Response::redirect()]] method. The method
sets the appropriate `Location` header with the given URL and returns the response object itself. In an action method,
you can call its shortcut version [[yii\web\Controller::redirect()]]. For example,
```php
public function actionOld()
{
return $this->redirect('http://example.com/new', 301);
}
```
In the above code, the action method returns the result of the `redirect()` method. As explained before, the response
object returned by an action method will be used as the response sending to end users.
In places other than an action method, you should call [[yii\web\Response::redirect()]] directly followed by
a call to the [[yii\web\Response::send()]] method to ensure no extra content will be appended to the response.
```php
\Yii::$app->response->redirect('http://example.com/new', 301)->send();
```
> Info: By default, the [[yii\web\Response::redirect()]] method sets the response status code to be 302 which instructs
the browser that the resource being requested is *temporarily* located in a different URI. You can pass in a status
code 301 to tell the browser that the resource has been *permanently* relocated.
When the current request is an AJAX request, sending a `Location` header will not automatically cause the browser
redirection. To solve this problem, the [[yii\web\Response::redirect()]] method sets an `X-Redirect` header with
the redirection URL as its value. On the client side you may write JavaScript code to read this header value and
redirect the browser accordingly.
> Info: Yii comes with a `yii.js` JavaScript file which provides a set of commonly used JavaScript utilities,
including browser redirection based on the `X-Redirect` header. Therefore, if you are using this JavaScript file
(by registering the [[yii\web\YiiAsset]] asset bundle), you do not need to write anything to support AJAX redirection.
## Sending Files <a name="sending-files"></a>
Like browser redirection, file sending is another feature that relies on specific HTTP headers. Yii provides
a set of methods to support various file sending needs. They all have built-in support for HTTP range header.
* [[yii\web\Response::sendFile()]]: sends an existing file to client.
* [[yii\web\Response::sendContentAsFile()]]: sends a text string as a file to client.
* [[yii\web\Response::sendStreamAsFile()]]: sends an existing file stream as a file to client.
These methods have the same method signature with the response object as the return value. If the file
to be sent is very big, you should consider using [[yii\web\Response::sendStreamAsFile()]] because it is more
memory efficient. The following example shows how to send a file in a controller action:
```php
public function actionDownload()
{
return \Yii::$app->response->sendFile('path/to/file.txt');
}
```
If you are calling the file sending method in places other than an action method, you should also call
the [[yii\web\Response::send()]] method afterwards to ensure no extra content will be appended to the response.
```php
\Yii::$app->response->sendFile('path/to/file.txt')->send();
```
Some Web servers have a special file sending support called *X-Sendfile*. The idea is to redirect the
request for a file to the Web server which will directly serve the file. As a result, the Web application
can terminate earlier while the Web server is sending the file. To use this feature, you may call
the [[yii\web\Response::xSendFile()]]. The following list summarizes how to enable the `X-Sendfile` feature
for some popular Web servers:
- Apache: [X-Sendfile](http://tn123.org/mod_xsendfile)
- Lighttpd v1.4: [X-LIGHTTPD-send-file](http://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)
- Lighttpd v1.5: [X-Sendfile](http://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file)
- Nginx: [X-Accel-Redirect](http://wiki.nginx.org/XSendfile)
- Cherokee: [X-Sendfile and X-Accel-Redirect](http://www.cherokee-project.com/doc/other_goodies.html#x-sendfile)
## Sending Response <a name="sending-response"></a>
The content in a response is not sent to the user until the [[yii\web\Response::send()]] method is called.
By default, this method will be called automatically at the end of [[yii\base\Application::run()]]. You can, however,
explicitly call this method to force sending out the response immediately.
The [[yii\web\Response::send()]] method takes the following steps to send out a response:
1. Trigger the [[yii\web\Response::EVENT_BEFORE_SEND]] event.
2. Call [[yii\web\Response::prepare()]] to format [[yii\web\Response::data|response data]] into
[[yii\web\Response::content|response content]].
3. Trigger the [[yii\web\Response::EVENT_AFTER_PREPARE]] event.
4. Call [[yii\web\Response::sendHeaders()]] to send out the registered HTTP headers.
5. Call [[yii\web\Response::sendContent()]] to send out the response body content.
6. Trigger the [[yii\web\Response::EVENT_AFTER_SEND]] event.
After the [[yii\web\Response::send()]] method is called once, any further call to this method will be ignored.
This means once the response is sent out, you will not be able to append more content to it.
As you can see, the [[yii\web\Response::send()]] method triggers several useful events. By responding to
these events, it is possible to adjust or decorate the response.
Routing
=======
When the [[yii\web\Application::run()|run()]] method is called by the [entry script](structure-entry-scripts.md),
the first thing it does is to resolve the incoming request and instantiate an appropriate
[controller action](structure-controllers.md) to handle the request. This process is called *routing*.
## Resolving Route <a name="resolving-route"></a>
The first step of routing is to parse the incoming request into a route which, as described in
the [Controllers](structure-controllers.md#routes) section, is used to address a controller action.
This is done by [[yii\web\Request::resolve()|resolve()]] method of the `request` application component.
The method invokes the [URL manager](runtime-url-handling.md) to do the actual request parsing work.
By default, if the incoming request contains a `GET` parameter named `r`, its value will be considered
as the route. However, if the [[yii\web\UrlManager::enablePrettyUrl|pretty URL feature]] is enabled,
more work will be done to determine the requested route. For more details, please refer to
the [URL Parsing and Generation](runtime-url-handling.md) section.
In case a route cannot be determined, the `request` component will throw a [[yii\web\NotFoundHttpException]].
### Default Route <a name="default-route"></a>
If an incoming request does not specify a route, which often happens to the request for homepages,
the route specified by [[yii\web\Application::defaultRoute]] will be used. The default value of this property
is `site/index`, which refers to the `index` action of the `site` controller. You may customize this property
in the application configuration like the following:
```php
return [
// ...
'defaultRoute' => 'main/index',
];
```
### `catchAll` Route <a name="catchall-route"></a>
Sometimes, you may want to put your Web application in maintenance mode temporarily and display the same
informational page for all requests. There are many ways to accomplish this goal. But one of the simplest
ways is to configure the [[yii\web\Application::catchAll]] property like the following in the application configuration:
```php
return [
// ...
'catchAll' => ['site/offline'],
];
```
The `catchAll` property should take an array whose first element specifies a route, and
the rest of the elements (name-value pairs) specify the parameters to be bound to the action.
When the `catchAll` property is set, it will replace any route resolved from the incoming requests.
With the above configuration, the same `site/offline` action will be used to handle all incoming requests.
## Creating Action <a name="creating-action"></a>
Once the requested route is determined, the next step is to create the action object corresponding to the route.
The route is broken down into multiple parts by the slashes in it. For example, `site/index` will be
broken into `site` and `index`. Each part is an ID which may refer to a module, a controller or an action.
Starting from the first part in the route, the application conducts the following steps to create modules (if any),
the controller and the action:
1. Set the application as the current module.
2. Check if the [[yii\base\Module::controllerMap|controller map]] of the current module contains the current ID.
If so, a controller object will be created according to the controller configuration found in the map,
and do Step 5 with the rest parts of the route.
3. Check if the ID refers to a module listed in the [[yii\base\Module::modules|modules]] property of
the current module. If so, a module is created according to the configuration found in the module list,
and do Step 2 with the next part in the route under the context of the newly created module.
4. Treat the ID as a controller ID and create a controller object. Do the next step with the rest part of
the route.
5. The controller looks for the current ID in its [[yii\base\Controller::actions()|action map]]. If found,
it creates an action according to the configuration found in the map. Otherwise, the controller will
attempt to create an inline action which is defined by an action method corresponding to the current ID.
Among the above steps, if any error occurs, a [[yii\web\NotFoundHttpException]] will be thrown, indicating
failure of the routing.
......@@ -3,7 +3,7 @@ Working with Databases
This section will describe how to create a new page that displays country data fetched from
a database table named `country`. To achieve this goal, you will configure a database connection,
create an [Active Record](db-active-record.md) class, and define an [action](structure-controllers.md),
create an [Active Record](db-active-record.md) class, define an [action](structure-controllers.md),
and create a [view](structure-views.md).
Through this tutorial, you will learn how to:
......
......@@ -123,8 +123,8 @@ If the model is successfully populated (i.e., if the user has submitted the HTML
provides components such as `request`, `response`, `db`, etc. to support specific functionality.
In the above code, the `request` component of the application instance is used to access the `$_POST` data.
If everything is fine, the action will render a view named `entry-confirm` to confirm the data entered
with the user that the data entered. If no data is submitted or the data contains errors, the `entry` view will
If everything is fine, the action will render a view named `entry-confirm` to confirm the successful submission
of the data to the user. If no data is submitted or the data contains errors, the `entry` view will
be rendered, wherein the HTML form will be shown, along with any validation error messages.
> Note: In this very simple example we just render the confirmation page upon valid data submission. In practice,
......
......@@ -4,7 +4,7 @@ Installing Yii
You can install Yii in two ways, using [Composer](http://getcomposer.org/) or by downloading an archive file.
The former is the preferred way, as it allows you to install new [extensions](extend-creating-extensions.md) or update Yii by simply running a single command.
> Note: Unlike with Yii 1, standard installations of Yii 2 results in both the framework and an application skeleton being downloaded and installed.
> Note: Unlike with Yii 1, standard installations of Yii 2 result in both, the framework and an application skeleton being downloaded and installed.
Installing via Composer <a name="installing-via-composer"></a>
......@@ -21,16 +21,24 @@ On Windows, you'll download and run [Composer-Setup.exe](https://getcomposer.org
Please refer to the [Composer Documentation](https://getcomposer.org/doc/) if you encounter any
problems or want to learn more about Composer usage.
If you had Composer already installed before, make sure you use an up to date version. You can update Composer
by running `composer self-update`.
With Composer installed, you can install Yii by running the following commands under a Web-accessible folder:
composer global require "fxp/composer-asset-plugin:1.0.0-beta1"
composer global require "fxp/composer-asset-plugin:1.0.0-beta2"
composer create-project --prefer-dist yiisoft/yii2-app-basic basic
The first command installs the [composer asset plugin](https://github.com/francoispluchino/composer-asset-plugin/)
which allows managing bower and npm package dependencies through composer. You only need to run this command
once for all. The second command installs Yii in a directory named `basic`.
which allows managing bower and npm package dependencies through Composer. You only need to run this command
once for all. The second command installs Yii in a directory named `basic`. You can choose a different directory name if you want.
> Note: During the installation it may happen that Composer asks for login credentials for your Github account because it hits the
> Github API rate-limit. This is normal because Composer needs to retrieve a lot of information for all the packages from Github.
> Logging in to Github increases the API rate-limit so Composer can go on with its work. For more details, please refer to the
> [Composer documentation](https://getcomposer.org/doc/articles/troubleshooting.md#api-rate-limit-and-oauth-tokens).
> Tip: If you want to install the latest development version of Yii, you may use the following command,
> Tip: If you want to install the latest development version of Yii, you may use the following command instead,
> which adds a [stability option](https://getcomposer.org/doc/04-schema.md#minimum-stability):
>
> composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic
......@@ -41,9 +49,9 @@ once for all. The second command installs Yii in a directory named `basic`.
Installing from an Archive File <a name="installing-from-archive-file"></a>
-------------------------------
Installing Yii from an archive file involves two steps:
Installing Yii from an archive file involves three steps:
1. Download the archive file from [yiiframework.com](http://www.yiiframework.com/download/yii2-basic).
1. Download the archive file from [yiiframework.com](https://github.com/yiisoft/yii2/releases/download/2.0.0-rc/yii-basic-app-2.0.0-rc.tgz).
2. Unpack the downloaded file to a Web-accessible folder.
3. Modify the `config/web.php` file by entering a secret key for the `cookieValidationKey` configuration item
(this is done automatically if you are installing Yii using Composer):
......@@ -135,8 +143,8 @@ should replace `path/to/basic/web` with the actual path for `basic/web`.
DocumentRoot "path/to/basic/web"
<Directory "path/to/basic/web">
# use mod_rewrite for pretty URL support
RewriteEngine on
# If a directory or a file exists, use the request directly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
......
......@@ -25,9 +25,9 @@ This section will summarize the Yii resources available to help you be more prod
* [Extensions](http://www.yiiframework.com/extensions/):
Yii boasts a library of thousands of user-contributed extensions that can be easily plugged into your applications, thereby making your application development even faster and easier.
* Community
- [Forum](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)
- Forum: <http://www.yiiframework.com/forum/>
- IRC chat: The #yii channel on the freenode network (<irc://irc.freenode.net/yii>)
- 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>
......@@ -240,15 +240,13 @@ be the corresponding [configuration arrays](concept-configurations.md).
> Tip: You can conditionally choose which assets to use in an asset bundle. The following example shows how
> to use `jquery.js` in the development environment and `jquery.min.js` otherwise:
>
```php
'yii\web\JqueryAsset' => [
'js' => [
YII_ENV_DEV ? 'jquery.js' : 'jquery.min.js'
]
],
```
> ```php
> 'yii\web\JqueryAsset' => [
> 'js' => [
> YII_ENV_DEV ? 'jquery.js' : 'jquery.min.js'
> ]
> ],
> ```
You can disable one or multiple asset bundles by associating `false` with the names of the asset bundles
that you want to disable. When you register a disabled asset bundle with a view, none of its dependent bundles
......
......@@ -17,7 +17,7 @@ To use an extension, you need to install it first. Most extensions are distribut
packages which can be installed by taking the following two simple steps:
1. modify the `composer.json` file of your application and specify which extensions (Composer packages) you want to install.
2. run `php composer.phar install` to install the specified extensions.
2. run `composer install` to install the specified extensions.
Note that you may need to install [Composer](https://getcomposer.org/) if you do not have it.
......@@ -172,7 +172,7 @@ We recommend you prefix `yii2-` to the project name for packages representing Yi
It is important that you specify the package type of your extension as `yii2-extension` so that the package can
be recognized as a Yii extension when being installed.
When a user runs `php composer.phar install` to install an extension, the file `vendor/yiisoft/extensions.php`
When a user runs `composer install` to install an extension, the file `vendor/yiisoft/extensions.php`
will be automatically updated to include the information about the new extension. From this file, Yii applications
can know which extensions are installed (the information can be accessed via [[yii\base\Application::extensions]].
......
......@@ -19,7 +19,7 @@ Gii is an official Yii extension. The preferred way to install this extension is
You can either run this command:
```
php composer.phar require --prefer-dist yiisoft/yii2-gii "*"
composer require "yiisoft/yii2-gii:*"
```
Or you can add this code to the require section of your `composer.json` file:
......
......@@ -12,19 +12,18 @@ Installation
### Install via Composer
If you do not have [Composer](http://getcomposer.org/), you may download it from
[http://getcomposer.org/](http://getcomposer.org/) or run the following command on Linux/Unix/MacOS:
If you do not have [Composer](http://getcomposer.org/), follow the instructions in the
[Installing Yii](start-installation.md#installing-via-composer) section to install it.
~~~
curl -sS http://getcomposer.org/installer | php
~~~
With Composer installed, you can then install the application using the following commands:
You can then install the application using the following command:
composer global require "fxp/composer-asset-plugin:1.0.0-beta2"
composer create-project --prefer-dist --stability=dev yiisoft/yii2-app-advanced yii-application
~~~
php composer.phar global require "fxp/composer-asset-plugin:1.0.0-beta1"
php composer.phar create-project --prefer-dist --stability=dev yiisoft/yii2-app-advanced /path/to/yii-application
~~~
The first command installs the [composer asset plugin](https://github.com/francoispluchino/composer-asset-plugin/)
which allows managing bower and npm package dependencies through Composer. You only need to run this command
once for all. The second command installs the advanced application in a directory named `yii-application`.
You can choose a different directory name if you want.
Getting started
---------------
......@@ -87,6 +86,8 @@ Predefined path aliases
- `@console` - console directory.
- `@runtime` - runtime directory of currently running web application.
- `@vendor` - Composer vendor directory.
- `@bower` - vendor directory that contains the [bower packages](http://bower.io/).
- `@npm` - vendor directory that contains [npm packages](https://www.npmjs.org/).
- `@web` - base URL of currently running web application.
- `@webroot` - web root directory of currently running web application.
......@@ -202,7 +203,7 @@ your project.
Now the interesting part. You can add more packages your application needs to the `require` section.
All these packages are coming from [packagist.org](https://packagist.org/) so feel free to browse the website for useful code.
After your `composer.json` is changed you can run `php composer.phar update --prefer-dist`, wait till packages are downloaded and
After your `composer.json` is changed you can run `composer update --prefer-dist`, wait till packages are downloaded and
installed and then just use them. Autoloading of classes will be handled automatically.
Creating links from backend to frontend
......
......@@ -7,8 +7,8 @@ Internationalization (I18N) refers to the process of designing a software applic
various languages and regions without engineering changes. For Web applications, this is of particular importance
because the potential users may be worldwide.
Yii offers several tools that help with internationalisation of a website such as [message translation][],
[number and date formatting][].
Yii offers several tools that help with internationalisation of a website such as message translation and
number- and date-formatting.
Locale and Language
-------------------
......
......@@ -48,6 +48,6 @@ Use the Template
That's all that's required to create a new Yii application template. Now you can create projects using your template:
```
php composer.phar global require "fxp/composer-asset-plugin:1.0.0-beta1"
php composer.phar create-project --prefer-dist --stability=dev mysoft/yii2-app-coolone new-project
composer global require "fxp/composer-asset-plugin:1.0.0-beta2"
composer create-project --prefer-dist --stability=dev mysoft/yii2-app-coolone new-project
```
......@@ -112,7 +112,7 @@ There are two ways of referencing templates in `include` and `extends` statement
```
{% include "comment.twig" %}
{% extends "post.twig" %
{% extends "post.twig" %}
{% include "@app/views/snippets/avatar.twig" %}
{% extends "@app/views/layouts/2columns.twig" %}
......
......@@ -17,7 +17,7 @@ Many third-party libraries are released in terms of [Composer](https://getcompos
You can install such libraries by taking the following two simple steps:
1. modify the `composer.json` file of your application and specify which Composer packages you want to install.
2. run `php composer.phar install` to install the specified packages.
2. run `composer install` to install the specified packages.
The classes in the installed Composer packages can be autoloaded using the Composer autoloader. Make sure
the [entry script](structure-entry-scripts.md) of your application contains the following lines to install
......@@ -83,8 +83,8 @@ If the third-party system uses Composer to manage its dependencies, you can simp
to install Yii:
```
php composer.phar require yiisoft/yii2-framework:*
php composer.phar install
composer require "yiisoft/yii2:*"
composer install
```
Otherwise, you can [download](http://www.yiiframework.com/download/) the Yii release file and unpack it in
......@@ -101,7 +101,8 @@ new yii\web\Application($yiiConfig); // Do NOT call run() here
As you can see, the code above is very similar to that in the [entry script](structure-entry-scripts.md) of
a typical Yii application. The only difference is that after the application instance is created, the `run()` method
is not called. This is because by calling `run()`, Yii will take over the control of the request handling workflow.
is not called. This is because by calling `run()`, Yii will take over the control of the request handling workflow
which is not needed in this case and already handled by the existing application.
Like in a Yii application, you should configure the application instance based on the environment running
the third-party system. For example, to use the [Active Record](db-active-record.md) feature, you need to configure
......@@ -118,9 +119,9 @@ the whole application in Yii 2, you may just want to enhance it using some of th
This can be achieved as described below.
> Note: Yii 2 requires PHP 5.4 or above. You should make sure that both your server and the existing application
support this.
> support this.
First, install Yii 2 in your existing application by following the instructions given in the last subsection.
First, install Yii 2 in your existing application by following the instructions given in the [last subsection](#using-yii-in-others).
Second, modify the entry script of the application as follows,
......@@ -149,13 +150,14 @@ require($yii1path . '/YiiBase.php'); // Yii 1.x
class Yii extends \yii\BaseYii
{
// copy-paste the code in YiiBase (1.x) here
// copy-paste the code from YiiBase (1.x) here
}
Yii::$classMap = include($yii2path . '/classes.php');
// register Yii2 autoloader via Yii1
Yii::registerAutoloader(['Yii', 'autoload']);
// create the dependency injection container
Yii::$container = new yii\di\Container;
```
That's all! Now in any part of your code, you can use `Yii::$app` to access the Yii 2 application instance, while
......
......@@ -24,7 +24,6 @@ convenient way to include bootstrap assets in your pages with a single line adde
public $depends = [
'yii\web\YiiAsset',
'yii\bootstrap\BootstrapAsset', // this line
// 'yii\bootstrap\BootstrapThemeAsset' // uncomment to apply bootstrap 2 style to bootstrap 3
];
```
......
......@@ -23,12 +23,13 @@ structure-models.md | Yes
structure-modules.md | Yes
structure-filters.md | Yes
structure-widgets.md | Yes
structure-assets.md |
structure-assets.md | Yes
structure-extensions.md | Yes
runtime-bootstrapping.md |
runtime-routing.md |
runtime-requests.md |
runtime-responses.md |
runtime-overview.md | Yes
runtime-bootstrapping.md | Yes
runtime-routing.md | Yes
runtime-requests.md | Yes
runtime-responses.md | Yes
runtime-sessions-cookies.md |
runtime-url-handling.md |
runtime-handling-errors.md |
......
Yii Framework 2 apidoc extension Change Log
===========================================
2.0.0-rc under development
--------------------------
2.0.0 under development
-----------------------
- no changes in this release.
2.0.0-rc September 27, 2014
---------------------------
- no changes in this release.
......
......@@ -41,6 +41,7 @@ class ApiMarkdownLaTeX extends GithubMarkdown
{
list($html, $offset) = $this->parseApiLinks($text);
// TODO allow break also on camel case
$latex = '\texttt{'.str_replace(['\\textbackslash', '::'], ['\allowbreak{}\\textbackslash', '\allowbreak{}::\allowbreak{}'], $this->escapeLatex(strip_tags($html))).'}';
return [$latex, $offset];
......
......@@ -36,6 +36,7 @@ trait ApiMarkdownTrait
// Collection resolves relative types
$typeName = (new Collection([$typeName], $context->phpDocContext))->__toString();
}
/** @var $type TypeDoc */
$type = static::$renderer->apiContext->getType($typeName);
if ($type === null) {
static::$renderer->apiContext->errors[] = [
......
......@@ -37,6 +37,21 @@ class TypeDoc extends BaseDoc
public $namespace;
/**
* Finds subject (method or property) by name
*
* If there is a property with the same as a method, the method will be returned if the name is not stated
* explicitly by prefixing with `$`.
*
* Example for method `attributes()` and property `$attributes` which both may exist:
*
* - `$subjectName = '$attributes'` finds a property or nothing.
* - `$subjectName = 'attributes()'` finds a method or nothing.
* - `$subjectName = 'attributes'` finds the method if it exists, if not it will find the property.
*
* @param $subjectName
* @return null|MethodDoc|PropertyDoc
*/
public function findSubject($subjectName)
{
if ($subjectName[0] != '$') {
......
Yii Framework 2 authclient extension Change Log
===============================================
2.0.0-rc under development
--------------------------
2.0.0 under development
-----------------------
- no changes in this release.
2.0.0-rc September 27, 2014
---------------------------
- Bug #3633: OpenId return URL comparison advanced to prevent url encode problem (klimov-paul)
- Bug #4490: `yii\authclient\widgets\AuthChoice` does not preserve initial settings while opening popup (klimov-paul)
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\bootstrap;
use yii\web\AssetBundle;
/**
* Asset bundle for the Twitter bootstrap default theme.
*
* @author Alexander Makarov <sam@rmcreative.ru>
* @since 2.0
*/
class BootstrapThemeAsset extends AssetBundle
{
public $sourcePath = '@bower/bootstrap/dist';
public $css = [
'css/bootstrap-theme.css',
];
public $depends = [
'yii\bootstrap\BootstrapAsset',
];
}
Yii Framework 2 bootstrap extension Change Log
==============================================
2.0.0-rc under development
--------------------------
2.0.0 under development
-----------------------
- Chg #5231: Collapse `items` property uses `label` element instead of array key for headers (nkovacs)
- Chg #5232: Collapse encodes headers by default (nkovacs)
- Chg #5217: Tabs no longer requires content since empty tab could be used dynamically (damiandennis)
2.0.0-rc September 27, 2014
---------------------------
- Bug #3292: Fixed dropdown widgets rendering incorrect HTML (it3rmit)
- Bug #3740: Fixed duplicate error message when client validation is enabled (tadaszelvys)
......
......@@ -20,13 +20,15 @@ use yii\helpers\Html;
* echo Collapse::widget([
* 'items' => [
* // equivalent to the above
* 'Collapsible Group Item #1' => [
* [
* 'label' => 'Collapsible Group Item #1',
* 'content' => 'Anim pariatur cliche...',
* // open its content by default
* 'contentOptions' => ['class' => 'in']
* ],
* // another group item
* 'Collapsible Group Item #2' => [
* [
* 'label' => 'Collapsible Group Item #1',
* 'content' => 'Anim pariatur cliche...',
* 'contentOptions' => [...],
* 'options' => [...],
......@@ -45,20 +47,22 @@ class Collapse extends Widget
* @var array list of groups in the collapse widget. Each array element represents a single
* group with the following structure:
*
* ```php
* // item key is the actual group header
* 'Collapsible Group Item #1' => [
* // required, the content (HTML) of the group
* 'content' => 'Anim pariatur cliche...',
* // optional the HTML attributes of the content group
* 'contentOptions' => [],
* // optional the HTML attributes of the group
* 'options' => [],
* ]
* - label: string, required, the group header label.
* - encode: boolean, optional, whether this label should be HTML-encoded. This param will override
* global `$this->encodeLabels` param.
* - content: string, required, the content (HTML) of the group
* - options: array, optional, the HTML attributes of the group
* - contentOptions: optional, the HTML attributes of the group's content
*
* ```
*/
public $items = [];
/**
* @var boolean whether the labels for header items should be HTML-encoded.
*/
public $encodeLabels = true;
/**
* Initializes the widget.
......@@ -88,7 +92,11 @@ class Collapse extends Widget
{
$items = [];
$index = 0;
foreach ($this->items as $header => $item) {
foreach ($this->items as $item) {
if (!isset($item['label'])) {
throw new InvalidConfigException("The 'label' option is required.");
}
$header = $item['label'];
$options = ArrayHelper::getValue($item, 'options', []);
Html::addCssClass($options, 'panel panel-default');
$items[] = Html::tag('div', $this->renderItem($header, $item, ++$index), $options);
......@@ -113,6 +121,11 @@ class Collapse extends Widget
$options['id'] = $id;
Html::addCssClass($options, 'panel-collapse collapse');
$encodeLabel = isset($item['encode']) ? $item['encode'] : $this->encodeLabels;
if ($encodeLabel) {
$header = Html::encode($header);
}
$headerToggle = Html::a($header, '#' . $id, [
'class' => 'collapse-toggle',
'data-toggle' => 'collapse',
......
......@@ -62,10 +62,10 @@ class Tabs extends Widget
* global `$this->encodeLabels` param.
* - headerOptions: array, optional, the HTML attributes of the tab header.
* - linkOptions: array, optional, the HTML attributes of the tab header link tags.
* - content: string, required if `items` is not set. The content (HTML) of the tab pane.
* - content: string, optional, the content (HTML) of the tab pane.
* - options: array, optional, the HTML attributes of the tab pane container.
* - active: boolean, optional, whether the item tab header and pane should be visible or not.
* - items: array, optional, if not set then `content` will be required. The `items` specify a dropdown items
* - items: array, optional, can be used instead of `content` to specify a dropdown items
* configuration array. Each item can hold three extra keys, besides the above ones:
* * active: boolean, optional, whether the item tab header and pane should be visible or not.
* * content: string, required if `items` is not set. The content (HTML) of the tab pane.
......@@ -168,8 +168,6 @@ class Tabs extends Widget
$linkOptions['data-toggle'] = 'tab';
$header = Html::a($label, '#' . $options['id'], $linkOptions);
$panes[] = Html::tag('div', $item['content'], $options);
} else {
throw new InvalidConfigException("Either the 'content' or 'items' option must be set.");
}
$headers[] = Html::tag('li', $header, $headerOptions);
......
......@@ -19,7 +19,7 @@
],
"require": {
"yiisoft/yii2": "*",
"bower-asset/bootstrap": "3.2.* | ~3.1"
"bower-asset/bootstrap": "3.2.* | 3.1.*"
},
"autoload": {
"psr-4": {
......
Yii Framework 2 Codeception extension Change Log
================================================
2.0.0-rc under development
--------------------------
2.0.0 under development
-----------------------
- no changes in this release.
2.0.0-rc September 27, 2014
---------------------------
- no changes in this release.
......
Yii Framework 2 composer extension Change Log
=============================================
2.0.0-rc under development
--------------------------
2.0.0 under development
-----------------------
- no changes in this release.
2.0.0-rc September 27, 2014
---------------------------
- Bug #3438: Fixed support for non-lowercase package names (cebe)
- Chg: Added `yii\composer\Installer::postCreateProject()` and modified the syntax of calling installer methods in composer.json (qiangxue)
......
Yii Framework 2 debug extension Change Log
==========================================
2.0.0-rc under development
--------------------------
2.0.0 under development
-----------------------
- no changes in this release.
2.0.0-rc September 27, 2014
---------------------------
- Bug #1263: Fixed the issue that Gii and Debug modules might be affected by incompatible asset manager configuration (qiangxue)
- Bug #3956: Debug toolbar was affecting flash message removal (samdark)
......
Yii Framework 2 elasticsearch extension Change Log
==================================================
2.0.0-rc under development
--------------------------
2.0.0 under development
-----------------------
- no changes in this release.
2.0.0-rc September 27, 2014
---------------------------
- Bug #3587: Fixed an issue with storing empty records (cebe)
- Bug #4187: Elasticsearch dynamic scripting is disabled in 1.2.0, so do not use it in query builder (cebe)
......
Yii Framework 2 faker extension Change Log
==============================================
2.0.0-rc under development
--------------------------
2.0.0 under development
-----------------------
- no changes in this release.
2.0.0-rc September 27, 2014
---------------------------
- Chg #4622: Simplified the way of creating a Faker fixture template file (qiangxue)
......
......@@ -101,20 +101,20 @@ use yii\helpers\VarDumper;
* //list all templates under specified template path
* yii fixture/templates --templatePath='@app/path/to/my/custom/templates'
* ~~~
*
*
* You also can create your own data providers for custom tables fields, see Faker library guide for more info (https://github.com/fzaninotto/Faker);
* After you created custom provider, for example:
*
* ~~~
* class Book extends \Faker\Provider\Base
* {
*
*
* public function title($nbWords = 5)
* {
* $sentence = $this->generator->sentence($nbWords);
* return mb_substr($sentence, 0, mb_strlen($sentence) - 1);
* }
*
*
* }
* ~~~
*
......@@ -131,6 +131,8 @@ use yii\helpers\VarDumper;
* ],
* ~~~
*
* @property \Faker\Generator $generator This property is read-only.
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0.0
*/
......
Yii Framework 2 gii extension Change Log
========================================
2.0.0-rc under development
--------------------------
2.0.0 under development
-----------------------
- no changes in this release.
2.0.0-rc September 27, 2014
---------------------------
- Bug #1263: Fixed the issue that Gii and Debug modules might be affected by incompatible asset manager configuration (qiangxue)
- Bug #2314: Gii model generator does not generate correct relation type in some special case (qiangxue)
......
......@@ -21,7 +21,7 @@
"yiisoft/yii2": "*",
"yiisoft/yii2-bootstrap": "*",
"phpspec/php-diff": ">=1.0.2",
"bower-asset/typeahead.js": "0.10.* | ~0.10"
"bower-asset/typeahead.js": "0.10.*"
},
"autoload": {
"psr-4": {
......
Yii Framework 2 imagine extension Change Log
================================================
2.0.0-rc under development
--------------------------
2.0.0 under development
-----------------------
- no changes in this release.
2.0.0-rc September 27, 2014
---------------------------
- no changes in this release.
......
......@@ -19,7 +19,7 @@
],
"require": {
"yiisoft/yii2": "*",
"imagine/imagine": "v0.5.0"
"imagine/imagine": "0.5.*"
},
"autoload": {
"psr-4": {
......
Yii Framework 2 jui extension Change Log
========================================
2.0.0-rc under development
--------------------------
2.0.0 under development
-----------------------
- no changes in this release.
2.0.0-rc September 27, 2014
---------------------------
- Chg #1551: Jui datepicker has a new property `$dateFormat` which is used to set the clientOption `dateFormat`.
The new property does not use the datepicker formatting synax anymore but uses the same as the `yii\i18n\Formatter`
......
......@@ -19,7 +19,7 @@
],
"require": {
"yiisoft/yii2": "*",
"bower-asset/jquery-ui": "1.11.*@stable | ~1.11@stable"
"bower-asset/jquery-ui": "1.11.*@stable"
},
"autoload": {
"psr-4": {
......
Yii Framework 2 mongodb extension Change Log
============================================
2.0.0-rc under development
--------------------------
2.0.0 under development
-----------------------
- no changes in this release.
2.0.0-rc September 27, 2014
---------------------------
- Bug #2337: `yii\mongodb\Collection::buildLikeCondition()` fixed to escape regular expression (klimov-paul)
- Bug #3385: Fixed "The 'connected' property is deprecated" (samdark)
......
Yii Framework 2 redis extension Change Log
==========================================
2.0.0-rc under development
--------------------------
2.0.0 under development
-----------------------
- no changes in this release.
2.0.0-rc September 27, 2014
---------------------------
- Bug #1311: Fixed storage and finding of `null` and boolean values (samdark, cebe)
- Enh #3520: Added `unlinkAll()`-method to active record to remove all records of a model relation (NmDimas, samdark, cebe)
......
Yii Framework 2 smarty extension Change Log
===========================================
2.0.0-rc under development
--------------------------
2.0.0 under development
-----------------------
- no changes in this release.
2.0.0-rc September 27, 2014
---------------------------
- Enh #4619 (samdark, hwmaier)
- New functions:
......
Yii Framework 2 sphinx extension Change Log
===========================================
2.0.0-rc under development
--------------------------
2.0.0 under development
-----------------------
- no changes in this release.
2.0.0-rc September 27, 2014
---------------------------
- Bug #3668: Escaping of the special characters at 'MATCH' statement added (klimov-paul)
- Bug #4018: AR relation eager loading does not work with db models (klimov-paul)
......
Yii Framework 2 swiftmailer extension Change Log
================================================
2.0.0-rc under development
--------------------------
2.0.0 under development
-----------------------
- no changes in this release.
2.0.0-rc September 27, 2014
---------------------------
- no changes in this release.
......
Yii Framework 2 twig extension Change Log
=========================================
2.0.0-rc under development
--------------------------
2.0.0 under development
-----------------------
- no changes in this release.
2.0.0-rc September 27, 2014
---------------------------
- Bug #2925: Fixed throwing exception when accessing AR property with null value (samdark)
- Bug #3767: Fixed repeated adding of extensions when using config. One may now pass extension instances as well (grachov)
......
Yii Framework 2 Change Log
==========================
2.0.0-rc under development
--------------------------
2.0.0 under development
-----------------------
- Bug #5260: `yii\i18n\Formatter::decimalSeparator` and `yii\i18n\Formatter::thousandSeparator` where not configurable when intl is not installed (execut, cebe)
- Bug: Date and time formatting now assumes UTC as the timezone for input dates unless a timezone is explicitly given (cebe)
- Enh #4275: Added `removeChildren()` to `yii\rbac\ManagerInterface` and implementations (samdark)
- Enh: Added `yii\base\Application::loadedModules` (qiangxue)
2.0.0-rc September 27, 2014
---------------------------
- Bug #1263: Fixed the issue that Gii and Debug modules might be affected by incompatible asset manager configuration (qiangxue)
- Bug #2314: Gii model generator does not generate correct relation type in some special case (qiangxue)
......@@ -617,6 +626,7 @@ Yii Framework 2 Change Log
- New: Added various authentication methods, including `HttpBasicAuth`, `HttpBearerAuth`, `QueryParamAuth`, and `CompositeAuth` (qiangxue)
- New: Added `HtmlResponseFormatter` and `JsonResponseFormatter` (qiangxue)
2.0.0-alpha, December 1, 2013
-----------------------------
......
......@@ -12,13 +12,14 @@ The preferred way to install the Yii framework is through [composer](http://getc
Either run
```
php composer.phar require --prefer-dist "yiisoft/yii2 *"
composer global require "fxp/composer-asset-plugin:1.0.0-beta2"
composer require --prefer-dist "yiisoft/yii2 *"
```
or add
```json
"yiisoft/yii2": "*"
"yiisoft/yii2": "*",
```
to the require section of your composer.json.
......@@ -8,6 +8,20 @@ if you want to upgrade from version A to version C and there is
version B between A and C, you need to following the instructions
for both A and B.
Upgrade from Yii 2.0 RC
-----------------------
* If you've implemented `yii\rbac\ManagerInterface` you need to add implementation for new method `removeChildren()`.
* The input dates for datetime formatting are now assumed to be in UTC unless a timezone is explicitly given.
Before, the timezone assumed for input dates was the default timezone set by PHP which is the same as `Yii::$app->timeZone`.
This causes trouble because the formatter uses `Yii::$app->timeZone` as the default values for output so no timezone conversion
was possible. If your timestamps are stored in the database without a timezone identifier you have to ensure they are in UTC or
add a timezone identifier explicitly.
* `yii\bootstrap\Collapse` is now encoding labels by default. `encode` item option and global `encodeLabels` property were
introduced to disable it. Keys are no longer used as labels. You need to remove keys and use `label` item option instead.
Upgrade from Yii 2.0 Beta
-------------------------
......@@ -16,7 +30,7 @@ Upgrade from Yii 2.0 Beta
the composer-asset-plugin, *before* you update your project:
```
php composer.phar global require "fxp/composer-asset-plugin:1.0.0-beta1"
php composer.phar global require "fxp/composer-asset-plugin:1.0.0-beta2"
```
You also need to add the following code to your project's `composer.json` file:
......
......@@ -180,6 +180,10 @@ abstract class Application extends Module
* This property is managed by the application. Do not modify this property.
*/
public $state;
/**
* @var array list of loaded modules indexed by their class names.
*/
public $loadedModules = [];
/**
......
......@@ -123,10 +123,6 @@ class Module extends ServiceLocator
* @var array child modules of this module
*/
private $_modules = [];
/**
* @var array list of currently requested modules indexed by their class names
*/
private static $_instances = [];
/**
......@@ -151,7 +147,7 @@ class Module extends ServiceLocator
public static function getInstance()
{
$class = get_called_class();
return isset(self::$_instances[$class]) ? self::$_instances[$class] : null;
return isset(Yii::$app->loadedModules[$class]) ? Yii::$app->loadedModules[$class] : null;
}
/**
......@@ -162,9 +158,9 @@ class Module extends ServiceLocator
public static function setInstance($instance)
{
if ($instance === null) {
unset(self::$_instances[get_called_class()]);
unset(Yii::$app->loadedModules[get_called_class()]);
} else {
self::$_instances[get_class($instance)] = $instance;
Yii::$app->loadedModules[get_class($instance)] = $instance;
}
}
......
......@@ -33,6 +33,7 @@ return [
'yii\base\InvalidConfigException' => YII2_PATH . '/base/InvalidConfigException.php',
'yii\base\InvalidParamException' => YII2_PATH . '/base/InvalidParamException.php',
'yii\base\InvalidRouteException' => YII2_PATH . '/base/InvalidRouteException.php',
'yii\base\InvalidValueException' => YII2_PATH . '/base/InvalidValueException.php',
'yii\base\Model' => YII2_PATH . '/base/Model.php',
'yii\base\ModelEvent' => YII2_PATH . '/base/ModelEvent.php',
'yii\base\Module' => YII2_PATH . '/base/Module.php',
......@@ -56,6 +57,7 @@ return [
'yii\behaviors\SluggableBehavior' => YII2_PATH . '/behaviors/SluggableBehavior.php',
'yii\behaviors\TimestampBehavior' => YII2_PATH . '/behaviors/TimestampBehavior.php',
'yii\caching\ApcCache' => YII2_PATH . '/caching/ApcCache.php',
'yii\caching\ArrayCache' => YII2_PATH . '/caching/ArrayCache.php',
'yii\caching\Cache' => YII2_PATH . '/caching/Cache.php',
'yii\caching\ChainedDependency' => YII2_PATH . '/caching/ChainedDependency.php',
'yii\caching\DbCache' => YII2_PATH . '/caching/DbCache.php',
......@@ -152,6 +154,7 @@ return [
'yii\helpers\BaseArrayHelper' => YII2_PATH . '/helpers/BaseArrayHelper.php',
'yii\helpers\BaseConsole' => YII2_PATH . '/helpers/BaseConsole.php',
'yii\helpers\BaseFileHelper' => YII2_PATH . '/helpers/BaseFileHelper.php',
'yii\helpers\BaseFormatConverter' => YII2_PATH . '/helpers/BaseFormatConverter.php',
'yii\helpers\BaseHtml' => YII2_PATH . '/helpers/BaseHtml.php',
'yii\helpers\BaseHtmlPurifier' => YII2_PATH . '/helpers/BaseHtmlPurifier.php',
'yii\helpers\BaseInflector' => YII2_PATH . '/helpers/BaseInflector.php',
......@@ -162,6 +165,7 @@ return [
'yii\helpers\BaseVarDumper' => YII2_PATH . '/helpers/BaseVarDumper.php',
'yii\helpers\Console' => YII2_PATH . '/helpers/Console.php',
'yii\helpers\FileHelper' => YII2_PATH . '/helpers/FileHelper.php',
'yii\helpers\FormatConverter' => YII2_PATH . '/helpers/FormatConverter.php',
'yii\helpers\Html' => YII2_PATH . '/helpers/Html.php',
'yii\helpers\HtmlPurifier' => YII2_PATH . '/helpers/HtmlPurifier.php',
'yii\helpers\Inflector' => YII2_PATH . '/helpers/Inflector.php',
......@@ -219,6 +223,7 @@ return [
'yii\rest\UrlRule' => YII2_PATH . '/rest/UrlRule.php',
'yii\rest\ViewAction' => YII2_PATH . '/rest/ViewAction.php',
'yii\test\ActiveFixture' => YII2_PATH . '/test/ActiveFixture.php',
'yii\test\ArrayFixture' => YII2_PATH . '/test/ArrayFixture.php',
'yii\test\BaseActiveFixture' => YII2_PATH . '/test/BaseActiveFixture.php',
'yii\test\DbFixture' => YII2_PATH . '/test/DbFixture.php',
'yii\test\Fixture' => YII2_PATH . '/test/Fixture.php',
......@@ -280,6 +285,7 @@ return [
'yii\web\RequestParserInterface' => YII2_PATH . '/web/RequestParserInterface.php',
'yii\web\Response' => YII2_PATH . '/web/Response.php',
'yii\web\ResponseFormatterInterface' => YII2_PATH . '/web/ResponseFormatterInterface.php',
'yii\web\ServerErrorHttpException' => YII2_PATH . '/web/ServerErrorHttpException.php',
'yii\web\Session' => YII2_PATH . '/web/Session.php',
'yii\web\SessionIterator' => YII2_PATH . '/web/SessionIterator.php',
'yii\web\TooManyRequestsHttpException' => YII2_PATH . '/web/TooManyRequestsHttpException.php',
......
......@@ -58,10 +58,10 @@
"yiisoft/yii2-composer": "*",
"ezyang/htmlpurifier": "4.6.*",
"cebe/markdown": "0.9.*",
"bower-asset/jquery": "2.1.*@stable | ~2.1@stable | ~1.11@stable",
"bower-asset/jquery.inputmask": "3.1.* | ~3.1",
"bower-asset/punycode": "1.3.* | ~1.3",
"bower-asset/yii2-pjax": "2.0.* | ~2.0"
"bower-asset/jquery": "2.1.*@stable | 1.11.*@stable",
"bower-asset/jquery.inputmask": "3.1.*",
"bower-asset/punycode": "1.3.*",
"bower-asset/yii2-pjax": "2.0.*"
},
"autoload": {
"psr-4": {"yii\\": ""}
......
......@@ -27,6 +27,9 @@ use yii\helpers\Console;
* where `<route>` is a route to a controller action and the params will be populated as properties of a command.
* See [[options()]] for details.
*
* @property string $help This property is read-only.
* @property string $helpSummary This property is read-only.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
......
......@@ -19,14 +19,14 @@ use yii\console\Exception;
* ~~~
* #see list of available components to flush
* yii cache
*
*
* #flush particular components specified by their names
* yii cache/flush first second third
*
*
* #flush all cache components that can be found in the system
* yii cache/flush-all
* ~~~
*
*
* @author Alexander Makarov <sam@rmcreative.ru>
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
......
......@@ -32,13 +32,13 @@ use yii\test\FixtureTrait;
*
* #append fixtures to already loaded
* yii fixture User --append
*
*
* #load fixtures with different namespace.
* yii fixture/load User --namespace=alias\my\custom\namespace\goes\here
* ~~~
*
* The `unload` sub-command can be used similarly to unload fixtures.
*
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
......
......@@ -118,8 +118,6 @@ use yii\caching\Cache;
* read-only.
* @property QueryBuilder $queryBuilder The query builder for the current DB connection. This property is
* read-only.
* @property array $queryCacheInfo The current query cache information, or null if query cache is not enabled.
* This property is read-only.
* @property Schema $schema The schema information for the database opened by this connection. This property
* is read-only.
* @property Connection $slave The currently active slave connection. Null is returned if there is slave
......
......@@ -120,7 +120,11 @@ class BaseArrayHelper
$next = array_shift($args);
foreach ($next as $k => $v) {
if (is_integer($k)) {
isset($res[$k]) ? $res[] = $v : $res[$k] = $v;
if (isset($res[$k])) {
$res[] = $v;
} else {
$res[$k] = $v;
}
} elseif (is_array($v) && isset($res[$k]) && is_array($res[$k])) {
$res[$k] = self::merge($res[$k], $v);
} else {
......
......@@ -8,7 +8,7 @@
namespace yii\i18n;
use DateTime;
use DateTimeInterface;
use DateTimeZone;
use IntlDateFormatter;
use NumberFormatter;
use Yii;
......@@ -20,8 +20,8 @@ use yii\helpers\HtmlPurifier;
use yii\helpers\Html;
/**
* Formatter provides a set of commonly used data formatting methods.
*
* Formatter provides a set of commonly used data formatting methods.
*
* The formatting methods provided by Formatter are all named in the form of `asXyz()`.
* The behavior of some of them may be configured via the properties of Formatter. For example,
* by configuring [[dateFormat]], one may control how [[asDate()]] formats the value into a date string.
......@@ -67,6 +67,9 @@ class Formatter extends Component
* e.g. `UTC`, `Europe/Berlin` or `America/Chicago`.
* Refer to the [php manual](http://www.php.net/manual/en/timezones.php) for available timezones.
* If this property is not set, [[\yii\base\Application::timeZone]] will be used.
*
* Note that the input timezone is assumed to be UTC always if no timezone is included in the input date value.
* Make sure to store datetime values in UTC in your database.
*/
public $timeZone;
/**
......@@ -205,8 +208,12 @@ class Formatter extends Component
}
$this->_intlLoaded = extension_loaded('intl');
if (!$this->_intlLoaded) {
$this->decimalSeparator = '.';
$this->thousandSeparator = ',';
if ($this->decimalSeparator === null) {
$this->decimalSeparator = '.';
}
if ($this->thousandSeparator === null) {
$this->thousandSeparator = ',';
}
}
}
......@@ -388,8 +395,9 @@ class Formatter extends Component
* types of value are supported:
*
* - an integer representing a UNIX timestamp
* - a string that can be parsed into a UNIX timestamp via `strtotime()`
* - a PHP DateTime object
* - a string that can be [parsed to create a DateTime object](http://php.net/manual/en/datetime.formats.php).
* The timestamp is assumed to be in UTC unless a timezone is explicitly given.
* - a PHP [DateTime](http://php.net/manual/en/class.datetime.php) object
*
* @param string $format the format used to convert the value into a date string.
* If null, [[dateFormat]] will be used.
......@@ -400,9 +408,9 @@ class Formatter extends Component
* Alternatively this can be a string prefixed with `php:` representing a format that can be recognized by the
* PHP [date()](http://php.net/manual/de/function.date.php)-function.
*
* @return string the formatted result.
* @throws InvalidParamException if the input value can not be evaluated as a date value.
* @throws InvalidConfigException if the date format is invalid.
* @return string the formatted result.
* @see dateFormat
*/
public function asDate($value, $format = null)
......@@ -419,8 +427,9 @@ class Formatter extends Component
* types of value are supported:
*
* - an integer representing a UNIX timestamp
* - a string that can be parsed into a UNIX timestamp via `strtotime()`
* - a PHP DateTime object
* - a string that can be [parsed to create a DateTime object](http://php.net/manual/en/datetime.formats.php).
* The timestamp is assumed to be in UTC unless a timezone is explicitly given.
* - a PHP [DateTime](http://php.net/manual/en/class.datetime.php) object
*
* @param string $format the format used to convert the value into a date string.
* If null, [[timeFormat]] will be used.
......@@ -431,9 +440,9 @@ class Formatter extends Component
* Alternatively this can be a string prefixed with `php:` representing a format that can be recognized by the
* PHP [date()](http://php.net/manual/de/function.date.php)-function.
*
* @return string the formatted result.
* @throws InvalidParamException if the input value can not be evaluated as a date value.
* @throws InvalidConfigException if the date format is invalid.
* @return string the formatted result.
* @see timeFormat
*/
public function asTime($value, $format = null)
......@@ -450,8 +459,9 @@ class Formatter extends Component
* types of value are supported:
*
* - an integer representing a UNIX timestamp
* - a string that can be parsed into a UNIX timestamp via `strtotime()`
* - a PHP DateTime object
* - a string that can be [parsed to create a DateTime object](http://php.net/manual/en/datetime.formats.php).
* The timestamp is assumed to be in UTC unless a timezone is explicitly given.
* - a PHP [DateTime](http://php.net/manual/en/class.datetime.php) object
*
* @param string $format the format used to convert the value into a date string.
* If null, [[dateFormat]] will be used.
......@@ -462,9 +472,9 @@ class Formatter extends Component
* Alternatively this can be a string prefixed with `php:` representing a format that can be recognized by the
* PHP [date()](http://php.net/manual/de/function.date.php)-function.
*
* @return string the formatted result.
* @throws InvalidParamException if the input value can not be evaluated as a date value.
* @throws InvalidConfigException if the date format is invalid.
* @return string the formatted result.
* @see datetimeFormat
*/
public function asDatetime($value, $format = null)
......@@ -486,7 +496,14 @@ class Formatter extends Component
];
/**
* @param integer $value normalized datetime value
* @param integer|string|DateTime $value the value to be formatted. The following
* types of value are supported:
*
* - an integer representing a UNIX timestamp
* - a string that can be [parsed to create a DateTime object](http://php.net/manual/en/datetime.formats.php).
* The timestamp is assumed to be in UTC unless a timezone is explicitly given.
* - a PHP [DateTime](http://php.net/manual/en/class.datetime.php) object
*
* @param string $format the format used to convert the value into a date string.
* @param string $type 'date', 'time', or 'datetime'.
* @throws InvalidConfigException if the date format is invalid.
......@@ -525,7 +542,7 @@ class Formatter extends Component
$format = FormatConverter::convertDateIcuToPhp($format, $type, $this->locale);
}
if ($this->timeZone != null) {
$timestamp->setTimezone(new \DateTimeZone($this->timeZone));
$timestamp->setTimezone(new DateTimeZone($this->timeZone));
}
return $timestamp->format($format);
}
......@@ -534,7 +551,14 @@ class Formatter extends Component
/**
* Normalizes the given datetime value as a DateTime object that can be taken by various date/time formatting methods.
*
* @param mixed $value the datetime value to be normalized.
* @param integer|string|DateTime $value the datetime value to be normalized. The following
* types of value are supported:
*
* - an integer representing a UNIX timestamp
* - a string that can be [parsed to create a DateTime object](http://php.net/manual/en/datetime.formats.php).
* The timestamp is assumed to be in UTC unless a timezone is explicitly given.
* - a PHP [DateTime](http://php.net/manual/en/class.datetime.php) object
*
* @return DateTime the normalized datetime value
* @throws InvalidParamException if the input value can not be evaluated as a date value.
*/
......@@ -549,17 +573,17 @@ class Formatter extends Component
}
try {
if (is_numeric($value)) { // process as unix timestamp
if (($timestamp = DateTime::createFromFormat('U', $value)) === false) {
if (($timestamp = DateTime::createFromFormat('U', $value, new DateTimeZone('UTC'))) === false) {
throw new InvalidParamException("Failed to parse '$value' as a UNIX timestamp.");
}
return $timestamp;
} elseif (($timestamp = DateTime::createFromFormat('Y-m-d', $value)) !== false) { // try Y-m-d format
} elseif (($timestamp = DateTime::createFromFormat('Y-m-d', $value, new DateTimeZone('UTC'))) !== false) { // try Y-m-d format (support invalid dates like 2012-13-01)
return $timestamp;
} elseif (($timestamp = DateTime::createFromFormat('Y-m-d H:i:s', $value)) !== false) { // try Y-m-d H:i:s format
} elseif (($timestamp = DateTime::createFromFormat('Y-m-d H:i:s', $value, new DateTimeZone('UTC'))) !== false) { // try Y-m-d H:i:s format (support invalid dates like 2012-13-01 12:63:12)
return $timestamp;
}
// finally try to create a DateTime object with the value
$timestamp = new DateTime($value);
$timestamp = new DateTime($value, new DateTimeZone('UTC'));
return $timestamp;
} catch(\Exception $e) {
throw new InvalidParamException("'$value' is not a valid date time value: " . $e->getMessage()
......@@ -624,7 +648,7 @@ class Formatter extends Component
return $this->nullDisplay;
}
} else {
$timezone = new \DateTimeZone($this->timeZone);
$timezone = new DateTimeZone($this->timeZone);
if ($referenceTime === null) {
$dateNow = new DateTime('now', $timezone);
......@@ -1010,7 +1034,9 @@ class Formatter extends Component
// disable grouping for edge cases like 1023 to get 1023 B instead of 1,023 B
$oldThousandSeparator = $this->thousandSeparator;
$this->thousandSeparator = '';
$options[NumberFormatter::GROUPING_USED] = false;
if ($this->_intlLoaded) {
$options[NumberFormatter::GROUPING_USED] = false;
}
// format the size value
$params = [
// this is the unformatted number used for the plural rule
......@@ -1071,18 +1097,18 @@ class Formatter extends Component
$formatter->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $decimals);
}
foreach ($this->numberFormatterOptions as $name => $value) {
$formatter->setAttribute($name, $value);
}
foreach ($options as $name => $value) {
$formatter->setAttribute($name, $value);
}
foreach ($this->numberFormatterTextOptions as $name => $attribute) {
$formatter->setTextAttribute($name, $attribute);
}
foreach ($textOptions as $name => $attribute) {
$formatter->setTextAttribute($name, $attribute);
}
foreach ($this->numberFormatterOptions as $name => $value) {
$formatter->setAttribute($name, $value);
}
foreach ($options as $name => $value) {
$formatter->setAttribute($name, $value);
}
return $formatter;
}
}
......@@ -17,8 +17,31 @@
* NOTE: this file must be saved in UTF-8 encoding.
*/
return [
'{nFormatted} B' => '{nFormatted} B',
'{nFormatted} GB' => '{nFormatted} Gb',
'{nFormatted} GiB' => '{nFormatted} GiB',
'{nFormatted} KB' => '{nFormatted} KB',
'{nFormatted} KiB' => '{nFormatted} KiB',
'{nFormatted} MB' => '{nFormatted} MB',
'{nFormatted} MiB' => '{nFormatted} MiB',
'{nFormatted} PB' => '{nFormatted} PB',
'{nFormatted} PiB' => '{nFormatted} PiB',
'{nFormatted} TB' => '{nFormatted} TB',
'{nFormatted} TiB' => '{nFormatted} TiB',
'{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, zero{baitu} one{baits} other{baiti}}',
'{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} gibi{n, plural, zero{baitu} one{baits} other{baiti}}',
'{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} giga{n, plural, zero{baitu} one{baits} other{baiti}}',
'{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} kibi{n, plural, zero{baitu} one{baits} other{baiti}}',
'{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} kilo{n, plural, zero{baitu} one{baits} other{baiti}}',
'{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} mebi{n, plural, zero{baitu} one{baits} other{baiti}}',
'{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} mega{n, plural, zero{baitu} one{baits} other{baiti}}',
'{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} pebi{n, plural, zero{baitu} one{baits} other{baiti}}',
'{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} peta{n, plural, zero{baitu} one{baits} other{baiti}}',
'{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} tebi{n, plural, zero{baitu} one{baits} other{baiti}}',
'{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} tera{n, plural, zero{baitu} one{baits} other{baiti}}',
'(not set)' => '(nav uzstādīts)',
'An internal server error occurred.' => 'Notika servera iekšēja kļūda.',
'Are you sure you want to delete this item?' => 'Vai jūs esat pārliecināti, ka vēlaties nodzēst šo elementu?',
'Delete' => 'Dzēst',
'Error' => 'Kļūda',
'File upload failed.' => 'Neizdevās augšupielādēt failu.',
......@@ -31,21 +54,23 @@ return [
'No help for unknown command "{command}".' => 'Palīdzība nezināmai komandai "{command}" nav pieejama.',
'No help for unknown sub-command "{command}".' => 'Palīdzība nezināmai sub-komandai "{command}" nav pieejama',
'No results found.' => 'Nekas nav atrasts.',
'Only files with these MIME types are allowed: {mimeTypes}.' => 'Ir atļauts augšupielādēt failus tikai ar sekojošiem MIME-tipiem: {mimeTypes}.',
'Only files with these extensions are allowed: {extensions}.' => 'Ir atļauts augšupielādēt failus tikai ar sekojošiem paplašinājumiem: {extensions}.',
'Page not found.' => 'Pieprasīta lapa netika atrasta.',
'Please fix the following errors:' => 'Nepieciešams izlabot sekojošas kļūdas:',
'Please upload a file.' => 'Lūdzu, augšupielādiet failu.',
'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Tiek rādīti ieraksti <b>{begin, number}-{end, number}</b> no <b>{totalCount, number}</b>.',
'The file "{file}" is not an image.' => 'Fails „{file}” nav uzskatīts par attēlu.',
'The file "{file}" is too big. Its size cannot exceed {limit, number} {limit, plural, one{byte} other{bytes}}.' => 'Fails „{file}” pārsniedz pieļaujamo ierobežojumu. Izmēram nedrīkst pārsniegt {limit, number} {limit, plural, one{baitu} few{baitus} many{baitus} other{baitus}}.',
'The file "{file}" is too small. Its size cannot be smaller than {limit, number} {limit, plural, one{byte} other{bytes}}.' => 'Fails „{file}” ir pārāk mazs. Izmēram ir jābūt vairāk par {limit, number} {limit, plural, one{baitu} few{baitus} many{baitus} other{baitus}}.',
'The file "{file}" is too big. Its size cannot exceed {limit, number} {limit, plural, one{byte} other{bytes}}.' => 'Fails „{file}” pārsniedz pieļaujamo ierobežojumu. Izmēram nedrīkst pārsniegt {limit, number} {limit, plural, one{baitu} other{baitus}}.',
'The file "{file}" is too small. Its size cannot be smaller than {limit, number} {limit, plural, one{byte} other{bytes}}.' => 'Fails „{file}” ir pārāk mazs. Izmēram ir jābūt vairāk par {limit, number} {limit, plural, one{baitu} other{baitiem}}.',
'The format of {attribute} is invalid.' => 'Vērtībai „{attribute}” ir nepareizs formāts.',
'The image "{file}" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Attēls „{file}” ir pārāk liels. Augstumam ir jābūt mazākam par {limit, number} {limit, plural, one{pikseļi} few{pikseļiem} many{pikseļiem} other{pikseļiem}}.',
'The image "{file}" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Attēls „{file}” ir pārāk liels. Platumam ir jābūt mazākam par {limit, number} {limit, plural, one{pikseļi} few{pikseļiem} many{pikseļiem} other{pikseļiem}}.',
'The image "{file}" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Attēls „{file}” ir pārāk mazs. Augstumam ir jābūt lielākam par {limit, number} {limit, plural, one{pikseļi} few{pikseļiem} many{pikseļiem} other{pikseļiem}}.',
'The image "{file}" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Attēls „{file}” ir pārāk mazs. Platumam ir jābūt lielākam par {limit, number} {limit, plural, one{pikseļi} few{pikseļiem} many{pikseļiem} other{pikseļiem}}.',
'The image "{file}" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Attēls „{file}” ir pārāk liels. Augstumam ir jābūt mazākam par {limit, number} {limit, plural, one{pikseļi} other{pikseļiem}}.',
'The image "{file}" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Attēls „{file}” ir pārāk liels. Platumam ir jābūt mazākam par {limit, number} {limit, plural, one{pikseļi} other{pikseļiem}}.',
'The image "{file}" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Attēls „{file}” ir pārāk mazs. Augstumam ir jābūt lielākam par {limit, number} {limit, plural, one{pikseļi} other{pikseļiem}}.',
'The image "{file}" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Attēls „{file}” ir pārāk mazs. Platumam ir jābūt lielākam par {limit, number} {limit, plural, one{pikseļi} other{pikseļiem}}.',
'The requested view "{name}" was not found.' => 'Pieprasīts priekšstata fails „{name}” nav atrasts.',
'The verification code is incorrect.' => 'Nepareizs pārbaudes kods.',
'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Kopā <b>{count, number}</b> {count, plural, one{ieraksts} few{ieraksti} many{ieraksti} other{ieraksti}}.',
'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Kopā <b>{count, number}</b> {count, plural, zero{ierakstu} one{ieraksts} other{ieraksti}}.',
'Unable to verify your data submission.' => 'Neizdevās pārbaudīt nosūtītos datus.',
'Unknown command "{command}".' => 'Nezināma komanda "{command}".',
'Unknown option: --{name}' => 'Nezināma opcija: --{name}',
......@@ -53,7 +78,13 @@ return [
'View' => 'Skatīties',
'Yes' => 'Jā',
'You are not allowed to perform this action.' => 'Jūs neesat autorizēts veikt šo darbību.',
'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Jūs nevarat augšupielādēt vairāk par {limit, number} {limit, plural, one{failu} few{failus} many{failus} other{failus}}.',
'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Jūs nevarat augšupielādēt vairāk par {limit, number} {limit, plural, one{failu} other{failiem}}.',
'in {delta, plural, =1{a day} other{# days}}' => 'pēc {delta, plural, =1{dienas} one{#. dienas} other{#. dienām}}',
'in {delta, plural, =1{a minute} other{# minutes}}' => 'pēc {delta, plural, =1{minūtes} one{#. minūtes} other{#. minūtēm}}',
'in {delta, plural, =1{a month} other{# months}}' => 'pēc {delta, plural, =1{mēneša} one{#. mēneša} other{# mēnešiem}}',
'in {delta, plural, =1{a second} other{# seconds}}' => 'pēc {delta, plural, =1{sekundes} one{#. sekundes} other{#. sekundēm}}',
'in {delta, plural, =1{a year} other{# years}}' => 'pēc {delta, plural, =1{gada} one{#. gada} other{#. gadām}}',
'in {delta, plural, =1{an hour} other{# hours}}' => 'pēc {delta, plural, =1{stundas} one{#. stundas} other{#. stundām}}',
'the input value' => 'ievadīta vērtība',
'{attribute} "{value}" has already been taken.' => '{attribute} „{value}” jau ir aizņemts.',
'{attribute} cannot be blank.' => 'Ir jāaizpilda „{attribute}”.',
......@@ -73,7 +104,13 @@ return [
'{attribute} must be no less than {min}.' => '„{attribute}” vērtībai ir jāpārsniedz {min}.',
'{attribute} must be repeated exactly.' => '„{attribute}” vērtībai ir precīzi jāatkārto.',
'{attribute} must not be equal to "{compareValue}".' => '„{attribute}” vērtībai nedrīkst būt vienādai ar „{compareValue}”.',
'{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '„{attribute}” vērtībai ir jāietver vismaz {min, number} {min, plural, one{simbolu} few{simbolus} many{simbolus} other{simbolus}}.',
'{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '„{attribute}” vērtībai ir jāietver ne vairāk par {max, number} {max, plural, one{simbolu} few{simbolus} many{simbolus} other{simbolus}}.',
'{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '„{attribute}” vērtībai ir jāietver {length, number} {length, plural, one{simbolu} few{simbolus} many{simbolus} other{simbolus}}.',
'{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '„{attribute}” vērtībai ir jāietver vismaz {min, number} {min, plural, one{simbolu} other{simbolus}}.',
'{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '„{attribute}” vērtībai ir jāietver ne vairāk par {max, number} {max, plural, one{simbolu} other{simbolus}}.',
'{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '„{attribute}” vērtībai ir jāietver {length, number} {length, plural, one{simbolu} other{simbolus}}.',
'{delta, plural, =1{a day} other{# days}} ago' => '{delta, plural, =1{diena} zero{# dienas} one{#. diena} other{#. dienas}} atpakaļ',
'{delta, plural, =1{a minute} other{# minutes}} ago' => '{delta, plural, =1{minūte} zero{# minūtes} one{#. minūte} other{#. minūtes}} atpakaļ',
'{delta, plural, =1{a month} other{# months}} ago' => '{delta, plural, =1{mēness} zero{# mēnešu} one{#. mēness} other{#. mēnešu}} atpakaļ',
'{delta, plural, =1{a second} other{# seconds}} ago' => '{delta, plural, =1{sekunde} zero{# sekundes} one{#. sekunde} other{#. sekundes}} atpakaļ',
'{delta, plural, =1{a year} other{# years}} ago' => '{delta, plural, =1{gads} zero{# gadi} one{#. gads} other{#. gadi}} atpakaļ',
'{delta, plural, =1{an hour} other{# hours}} ago' => '{delta, plural, =1{stunda} zero{# stundas} one{#. stunda} other{#. stundas}} atpakaļ',
];
......@@ -30,17 +30,17 @@ return [
'{nFormatted} PiB' => '{nFormatted} PiB',
'{nFormatted} TB' => '{nFormatted} TB',
'{nFormatted} TiB' => '{nFormatted} TiB',
'{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{# bajt} other{# bajtov}}',
'{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibibajt} other{gibibajtov}}',
'{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabajt} other{gigabajtov}}',
'{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibibajt} other{kibibajtov}}',
'{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobajt} other{kilobajtov}}',
'{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebibajt} other{mebibajtov}}',
'{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabajt} other{megabajtov}}',
'{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebibajt} other{pebibajtov}}',
'{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabajt} other{petabajtov}}',
'{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{tebibajt} other{tebibajtov}}',
'{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabajt} other{terabajtov}}',
'{nFormatted} {n, plural, =1{byte} other{bytes}}' => '{nFormatted} {n, plural, =1{bajt} =2{bajty} =3{bajty} =4{bajty} other{bajtov}}',
'{nFormatted} {n, plural, =1{gibibyte} other{gibibytes}}' => '{nFormatted} {n, plural, =1{gibibajt} =2{gibibajty} =3{gibibajty} =4{gibibajty} other{gibibajtov}}',
'{nFormatted} {n, plural, =1{gigabyte} other{gigabytes}}' => '{nFormatted} {n, plural, =1{gigabajt} =2{gigabajty} =3{gigabajty} =4{gigabajty} other{gigabajtov}}',
'{nFormatted} {n, plural, =1{kibibyte} other{kibibytes}}' => '{nFormatted} {n, plural, =1{kibibajt} =2{kibibajty} =3{kibibajty} =4{kibibajty} other{kibibajtov}}',
'{nFormatted} {n, plural, =1{kilobyte} other{kilobytes}}' => '{nFormatted} {n, plural, =1{kilobajt} =2{kilobajty} =3{kilobajty} =4{kilobajty} other{kilobajtov}}',
'{nFormatted} {n, plural, =1{mebibyte} other{mebibytes}}' => '{nFormatted} {n, plural, =1{mebibajt} =2{mebibajty} =3{mebibajty} =4{mebibajty} other{mebibajtov}}',
'{nFormatted} {n, plural, =1{megabyte} other{megabytes}}' => '{nFormatted} {n, plural, =1{megabajt} =2{megabajty} =3{megabajty} =4{megabajty} other{megabajtov}}',
'{nFormatted} {n, plural, =1{pebibyte} other{pebibytes}}' => '{nFormatted} {n, plural, =1{pebibajt} =2{pebibajty} =3{pebibajty} =4{pebibajty} other{pebibajtov}}',
'{nFormatted} {n, plural, =1{petabyte} other{petabytes}}' => '{nFormatted} {n, plural, =1{petabajt} =2{petabajty} =3{petabajty} =4{petabajty} other{petabajtov}}',
'{nFormatted} {n, plural, =1{tebibyte} other{tebibytes}}' => '{nFormatted} {n, plural, =1{tebibajt} =2{tebibajty} =3{tebibajty} =4{tebibajty} other{tebibajtov}}',
'{nFormatted} {n, plural, =1{terabyte} other{terabytes}}' => '{nFormatted} {n, plural, =1{terabajt} =2{terabajty} =3{terabajty} =4{terabajty} other{terabajtov}}',
'(not set)' => '(nie je nastavené)',
'An internal server error occurred.' => 'Vyskytla sa interná chyba servera.',
'Are you sure you want to delete this item?' => 'Skutočne chcete odstrániť tento záznam?',
......
......@@ -542,6 +542,16 @@ class DbManager extends BaseManager
/**
* @inheritdoc
*/
public function removeChildren($parent)
{
return $this->db->createCommand()
->delete($this->itemChildTable, ['parent' => $parent->name])
->execute() > 0;
}
/**
* @inheritdoc
*/
public function hasChild($parent, $child)
{
return (new Query)
......
......@@ -146,6 +146,14 @@ interface ManagerInterface
public function removeChild($parent, $child);
/**
* Removed all children form their parent.
* Note, the children items are not deleted. Only the parent-child relationships are removed.
* @param Item $parent
* @return boolean whether the removal is successful
*/
public function removeChildren($parent);
/**
* Returns a value indicating whether the child already exists for the parent.
* @param Item $parent
* @param Item $child
......
......@@ -218,6 +218,20 @@ class PhpManager extends BaseManager
/**
* @inheritdoc
*/
public function removeChildren($parent)
{
if (isset($this->children[$parent->name])) {
unset($this->children[$parent->name]);
$this->saveItems();
return true;
} else {
return false;
}
}
/**
* @inheritdoc
*/
public function hasChild($parent, $child)
{
return isset($this->children[$parent->name][$child->name]);
......
......@@ -13,7 +13,7 @@ use yii\base\InvalidConfigException;
/**
* ArrayFixture represents arbitrary fixture that can be loaded from PHP files.
*
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
......
......@@ -31,6 +31,9 @@ use yii\helpers\Url;
* ]
* ```
*
* @property AssetConverterInterface $converter The asset converter. Note that the type of this property
* differs in getter and setter. See [[getConverter()]] and [[setConverter()]] for details.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
......
......@@ -142,7 +142,7 @@ class HeaderCollection extends Object implements \IteratorAggregate, \ArrayAcces
/**
* Removes a header.
* @param string $name the name of the header to be removed.
* @return string the value of the removed header. Null is returned if the header does not exist.
* @return array the value of the removed header. Null is returned if the header does not exist.
*/
public function remove($name)
{
......@@ -150,7 +150,6 @@ class HeaderCollection extends Object implements \IteratorAggregate, \ArrayAcces
if (isset($this->_headers[$name])) {
$value = $this->_headers[$name];
unset($this->_headers[$name]);
return $value;
} else {
return null;
......
......@@ -65,7 +65,6 @@ use yii\helpers\StringHelper;
* @property string $queryString Part of the request URL that is after the question mark. This property is
* read-only.
* @property string $rawBody The request body. This property is read-only.
* @property string $rawCsrfToken The random token for CSRF validation. This property is read-only.
* @property string $referrer URL referrer, null if not present. This property is read-only.
* @property string $scriptFile The entry script file path.
* @property string $scriptUrl The relative URL of the entry script.
......
......@@ -35,6 +35,7 @@ use yii\helpers\StringHelper;
* ~~~
*
* @property CookieCollection $cookies The cookie collection. This property is read-only.
* @property string $downloadHeaders The attachment file name. This property is write-only.
* @property HeaderCollection $headers The header collection. This property is read-only.
* @property boolean $isClientError Whether this response indicates a client error. This property is
* read-only.
......
......@@ -625,8 +625,9 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
* @param mixed $defaultValue value to be returned if the flash message does not exist.
* @param boolean $delete whether to delete this flash message right after this method is called.
* If false, the flash message will be automatically deleted in the next request.
* @return mixed the flash message
* @return mixed the flash message or an array of messages if addFlash was used
* @see setFlash()
* @see addFlash()
* @see hasFlash()
* @see getAllFlashes()
* @see removeFlash()
......@@ -664,13 +665,16 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
*
* With the above code you can use the [bootstrap alert][] classes such as `success`, `info`, `danger`
* as the flash message key to influence the color of the div.
*
* Note that if you use [[addFlash()]], `$message` will be an array, and you will have to adjust the above code.
*
* [bootstrap alert]: http://getbootstrap.com/components/#alerts
*
* @param boolean $delete whether to delete the flash messages right after this method is called.
* If false, the flash messages will be automatically deleted in the next request.
* @return array flash messages (key => message).
* @return array flash messages (key => message or key => [message1, message2]).
* @see setFlash()
* @see addFlash()
* @see getFlash()
* @see hasFlash()
* @see removeFlash()
......@@ -712,6 +716,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
* regardless if it is accessed or not. If true (default value), the flash message will remain until after
* it is accessed.
* @see getFlash()
* @see addFlash()
* @see removeFlash()
*/
public function setFlash($key, $value = true, $removeAfterAccess = true)
......@@ -732,6 +737,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
* regardless if it is accessed or not. If true (default value), the flash message will remain until after
* it is accessed.
* @see getFlash()
* @see setFlash()
* @see removeFlash()
*/
public function addFlash($key, $value = true, $removeAfterAccess = true)
......@@ -758,6 +764,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
* @return mixed the removed flash message. Null if the flash message does not exist.
* @see getFlash()
* @see setFlash()
* @see addFlash()
* @see removeAllFlashes()
*/
public function removeFlash($key)
......@@ -777,6 +784,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
* by this method.
* @see getFlash()
* @see setFlash()
* @see addFlash()
* @see removeFlash()
*/
public function removeAllFlashes()
......
......@@ -283,6 +283,9 @@ class FormatterTest extends TestCase
public function testIntlDateRangeLow()
{
if (PHP_INT_SIZE == 4) { // 32bit systems
$this->markTestSkipped('intl does not support high date ranges on 32bit systems.');
}
$this->testDateRangeLow();
}
......@@ -298,6 +301,9 @@ class FormatterTest extends TestCase
public function testIntlDateRangeHigh()
{
if (PHP_INT_SIZE == 4) { // 32bit systems
$this->markTestSkipped('intl does not support high date ranges on 32bit systems.');
}
$this->testDateRangeHigh();
}
......@@ -483,6 +489,90 @@ class FormatterTest extends TestCase
}
public function provideTimezones()
{
return [
['UTC'],
['Europe/Berlin'],
['America/Jamaica'],
];
}
/**
* provide default timezones times input date value
*/
public function provideTimesAndTz()
{
$result = [];
foreach($this->provideTimezones() as $tz) {
$result[] = [$tz[0], 1407674460, 1388580060];
$result[] = [$tz[0], '2014-08-10 12:41:00', '2014-01-01 12:41:00'];
$result[] = [$tz[0], '2014-08-10 12:41:00 UTC', '2014-01-01 12:41:00 UTC'];
$result[] = [$tz[0], '2014-08-10 14:41:00 Europe/Berlin', '2014-01-01 13:41:00 Europe/Berlin'];
$result[] = [$tz[0], '2014-08-10 14:41:00 CEST', '2014-01-01 13:41:00 CET'];
$result[] = [$tz[0], '2014-08-10 14:41:00+0200', '2014-01-01 13:41:00+0100'];
$result[] = [$tz[0], '2014-08-10 14:41:00+02:00', '2014-01-01 13:41:00+01:00'];
$result[] = [$tz[0], '2014-08-10 14:41:00 +0200', '2014-01-01 13:41:00 +0100'];
$result[] = [$tz[0], '2014-08-10 14:41:00 +02:00', '2014-01-01 13:41:00 +01:00'];
$result[] = [$tz[0], '2014-08-10T14:41:00+02:00', '2014-01-01T13:41:00+01:00']; // ISO 8601
}
return $result;
}
/**
* Test timezones with input date and time in other timezones
* @dataProvider provideTimesAndTz
*/
public function testIntlTimezoneInput($defaultTz, $inputTimeDst, $inputTimeNonDst)
{
$this->testTimezoneInput($defaultTz, $inputTimeDst, $inputTimeNonDst);
}
/**
* Test timezones with input date and time in other timezones
* @dataProvider provideTimesAndTz
*/
public function testTimezoneInput($defaultTz, $inputTimeDst, $inputTimeNonDst)
{
date_default_timezone_set($defaultTz); // formatting has to be independent of the default timezone set by PHP
$this->formatter->datetimeFormat = 'yyyy-MM-dd HH:mm:ss';
$this->formatter->dateFormat = 'yyyy-MM-dd';
$this->formatter->timeFormat = 'HH:mm:ss';
// daylight saving time
$this->formatter->timeZone = 'UTC';
$this->assertSame('2014-08-10 12:41:00', $this->formatter->asDatetime($inputTimeDst));
$this->assertSame('2014-08-10', $this->formatter->asDate($inputTimeDst));
$this->assertSame('12:41:00', $this->formatter->asTime($inputTimeDst));
$this->assertSame('1407674460', $this->formatter->asTimestamp($inputTimeDst));
$this->formatter->timeZone = 'Europe/Berlin';
$this->assertSame('2014-08-10 14:41:00', $this->formatter->asDatetime($inputTimeDst));
$this->assertSame('2014-08-10', $this->formatter->asDate($inputTimeDst));
$this->assertSame('14:41:00', $this->formatter->asTime($inputTimeDst));
$this->assertSame('1407674460', $this->formatter->asTimestamp($inputTimeDst));
// non daylight saving time
$this->formatter->timeZone = 'UTC';
$this->assertSame('2014-01-01 12:41:00', $this->formatter->asDatetime($inputTimeNonDst));
$this->assertSame('2014-01-01', $this->formatter->asDate($inputTimeNonDst));
$this->assertSame('12:41:00', $this->formatter->asTime($inputTimeNonDst));
$this->assertSame('1388580060', $this->formatter->asTimestamp($inputTimeNonDst));
$this->formatter->timeZone = 'Europe/Berlin';
$this->assertSame('2014-01-01 13:41:00', $this->formatter->asDatetime($inputTimeNonDst));
$this->assertSame('2014-01-01', $this->formatter->asDate($inputTimeNonDst));
$this->assertSame('13:41:00', $this->formatter->asTime($inputTimeNonDst));
$this->assertSame('1388580060', $this->formatter->asTimestamp($inputTimeNonDst));
// tests for relative time
if ($inputTimeDst !== 1407674460) {
$this->assertSame('3 hours ago', $this->formatter->asRelativeTime($inputTimeDst, $relativeTime = str_replace(['14:41', '12:41'], ['17:41', '15:41'], $inputTimeDst)));
$this->assertSame('in 3 hours', $this->formatter->asRelativeTime($relativeTime, $inputTimeDst));
$this->assertSame('3 hours ago', $this->formatter->asRelativeTime($inputTimeNonDst, $relativeTime = str_replace(['13:41', '12:41'], ['16:41', '15:41'], $inputTimeNonDst)));
$this->assertSame('in 3 hours', $this->formatter->asRelativeTime($relativeTime, $inputTimeNonDst));
}
}
// number format
......@@ -666,6 +756,21 @@ class FormatterTest extends TestCase
$this->assertSame($this->formatter->nullDisplay, $this->formatter->asCurrency(null));
}
/**
* https://github.com/yiisoft/yii2/pull/5261
*/
public function testIntlIssue5261()
{
$this->formatter->locale = 'en-US';
$this->formatter->numberFormatterOptions = [
\NumberFormatter::FRACTION_DIGITS => 0
];
$this->formatter->numberFormatterTextOptions = [
\NumberFormatter::CURRENCY_CODE => 'EUR'
];
$this->assertSame('€100', $this->formatter->asCurrency(100, 'EUR'));
}
public function testAsCurrency()
{
$this->formatter->currencyCode = 'USD';
......@@ -762,6 +867,7 @@ class FormatterTest extends TestCase
// tests for base 1000
$this->formatter->sizeFormatBase = 1000;
$this->assertSame("999 B", $this->formatter->asShortSize(999));
$this->assertSame("999 B", $this->formatter->asShortSize('999'));
$this->assertSame("1.05 MB", $this->formatter->asShortSize(1024 * 1024));
$this->assertSame("1 KB", $this->formatter->asShortSize(1000));
$this->assertSame("1.02 KB", $this->formatter->asShortSize(1023));
......@@ -784,7 +890,7 @@ class FormatterTest extends TestCase
$this->assertSame('0 B', $this->formatter->asShortSize(0));
// null display
$this->assertSame($this->formatter->nullDisplay, $this->formatter->asSize(null));
$this->assertSame($this->formatter->nullDisplay, $this->formatter->asShortSize(null));
}
public function testAsShortSize()
......@@ -792,6 +898,7 @@ class FormatterTest extends TestCase
// tests for base 1000
$this->formatter->sizeFormatBase = 1000;
$this->assertSame("999 B", $this->formatter->asShortSize(999));
$this->assertSame("999 B", $this->formatter->asShortSize('999'));
$this->assertSame("1.05 MB", $this->formatter->asShortSize(1024 * 1024));
$this->assertSame("1.0486 MB", $this->formatter->asShortSize(1024 * 1024, 4));
$this->assertSame("1.00 KB", $this->formatter->asShortSize(1000));
......@@ -811,10 +918,10 @@ class FormatterTest extends TestCase
$this->assertSame("1,001 KiB", $this->formatter->asShortSize(1025, 3));
// empty values
$this->assertSame('0 bytes', $this->formatter->asSize(0));
$this->assertSame('0 B', $this->formatter->asShortSize(0));
// null display
$this->assertSame($this->formatter->nullDisplay, $this->formatter->asSize(null));
$this->assertSame($this->formatter->nullDisplay, $this->formatter->asShortSize(null));
}
public function testIntlAsSize()
......@@ -827,6 +934,7 @@ class FormatterTest extends TestCase
// tests for base 1000
$this->formatter->sizeFormatBase = 1000;
$this->assertSame("999 bytes", $this->formatter->asSize(999));
$this->assertSame("999 bytes", $this->formatter->asSize('999'));
$this->assertSame("1.05 megabytes", $this->formatter->asSize(1024 * 1024));
$this->assertSame("1 kilobyte", $this->formatter->asSize(1000));
$this->assertSame("1.02 kilobytes", $this->formatter->asSize(1023));
......@@ -853,6 +961,7 @@ class FormatterTest extends TestCase
// tests for base 1000
$this->formatter->sizeFormatBase = 1000;
$this->assertSame("999 bytes", $this->formatter->asSize(999));
$this->assertSame("999 bytes", $this->formatter->asSize('999'));
$this->assertSame("1.05 megabytes", $this->formatter->asSize(1024 * 1024));
$this->assertSame("1.0486 megabytes", $this->formatter->asSize(1024 * 1024, 4));
$this->assertSame("1.00 kilobyte", $this->formatter->asSize(1000));
......@@ -878,8 +987,10 @@ class FormatterTest extends TestCase
public function testIntlAsSizeConfiguration()
{
$this->assertSame("1023 bytes", $this->formatter->asSize(1023));
$this->assertSame("1023 B", $this->formatter->asShortSize(1023));
$this->formatter->thousandSeparator = '.';
$this->assertSame("1023 bytes", $this->formatter->asSize(1023));
$this->assertSame("1023 B", $this->formatter->asShortSize(1023));
}
/**
......@@ -888,7 +999,9 @@ class FormatterTest extends TestCase
public function testAsSizeConfiguration()
{
$this->assertSame("1023 bytes", $this->formatter->asSize(1023));
$this->assertSame("1023 B", $this->formatter->asShortSize(1023));
$this->formatter->thousandSeparator = '.';
$this->assertSame("1023 bytes", $this->formatter->asSize(1023));
$this->assertSame("1023 B", $this->formatter->asShortSize(1023));
}
}
......@@ -80,6 +80,8 @@ class DateValidatorTest extends TestCase
$this->assertTrue($val->validate('2013-09-13'));
$this->assertFalse($val->validate('31.7.2013'));
$this->assertFalse($val->validate('31-7-2013'));
$this->assertFalse($val->validate('asdasdfasfd'));
$this->assertFalse($val->validate(''));
$this->assertFalse($val->validate(time()));
$val->format = 'php:U';
$this->assertTrue($val->validate(time()));
......@@ -94,6 +96,8 @@ class DateValidatorTest extends TestCase
$this->assertTrue($val->validate('2013-09-13'));
$this->assertFalse($val->validate('31.7.2013'));
$this->assertFalse($val->validate('31-7-2013'));
$this->assertFalse($val->validate('asdasdfasfd'));
$this->assertFalse($val->validate(''));
$this->assertFalse($val->validate(time()));
$val->format = 'dd.MM.yyyy';
$this->assertTrue($val->validate('31.7.2013'));
......@@ -101,7 +105,7 @@ class DateValidatorTest extends TestCase
$this->assertTrue($val->validate('2009-02-15 15:16:17'));
}
public function testIntlValidateAttributePHP()
public function testIntlValidateAttributePHPFormat()
{
$this->testValidateAttributePHPFormat();
}
......@@ -138,7 +142,7 @@ class DateValidatorTest extends TestCase
}
public function testIntlValidateAttributeICU()
public function testIntlValidateAttributeICUFormat()
{
$this->testValidateAttributeICUFormat();
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment