Commit 3c518c42 by Qiang Xue

Merge commit 'f605508b' into feature-container

parents aae8622f f605508b
...@@ -76,12 +76,13 @@ There are two ActiveRecord methods for querying data from database: ...@@ -76,12 +76,13 @@ There are two ActiveRecord methods for querying data from database:
- [[yii\db\ActiveRecord::find()]] - [[yii\db\ActiveRecord::find()]]
- [[yii\db\ActiveRecord::findBySql()]] - [[yii\db\ActiveRecord::findBySql()]]
Both methods return an [[yii\db\ActiveQuery]] instance, which extends [[yii\db\Query]], and thus supports the same set of flexible and powerful DB query methods. The following examples demonstrate some of the possibilities. Both methods return an [[yii\db\ActiveQuery]] instance, which extends [[yii\db\Query]], and thus supports the same set
of flexible and powerful DB query methods. The following examples demonstrate some of the possibilities.
```php ```php
// to retrieve all *active* customers and order them by their ID: // to retrieve all *active* customers and order them by their ID:
$customers = Customer::find() $customers = Customer::find()
->where(['status' => $active]) ->where(['status' => Customer::STATUS_ACTIVE])
->orderBy('id') ->orderBy('id')
->all(); ->all();
...@@ -99,7 +100,7 @@ $customers = Customer::findBySql($sql)->all(); ...@@ -99,7 +100,7 @@ $customers = Customer::findBySql($sql)->all();
// to return the number of *active* customers: // to return the number of *active* customers:
$count = Customer::find() $count = Customer::find()
->where(['status' => $active]) ->where(['status' => Customer::STATUS_ACTIVE])
->count(); ->count();
// to return customers in terms of arrays rather than `Customer` objects: // to return customers in terms of arrays rather than `Customer` objects:
...@@ -113,6 +114,10 @@ $customers = Customer::find()->indexBy('id')->all(); ...@@ -113,6 +114,10 @@ $customers = Customer::find()->indexBy('id')->all();
// $customers array is indexed by customer IDs // $customers array is indexed by customer IDs
``` ```
> Note: In the code above `Customer::STATUS_ACTIVE` is a constant defined in the class itself. It is a good practice to
use such approach instead of relying on hardcoded strings and numbers directly.
Batch query is also supported when working with Active Record. For example, Batch query is also supported when working with Active Record. For example,
```php ```php
...@@ -193,6 +198,15 @@ Customer::updateAllCounters(['age' => 1]); ...@@ -193,6 +198,15 @@ Customer::updateAllCounters(['age' => 1]);
> Info: The `save()` method will either perform an `INSERT` or `UPDATE` SQL statement, depending > Info: The `save()` method will either perform an `INSERT` or `UPDATE` SQL statement, depending
on whether the ActiveRecord being saved is new or not by checking `ActiveRecord::isNewRecord`. on whether the ActiveRecord being saved is new or not by checking `ActiveRecord::isNewRecord`.
In order to load default values from database schema you may call `loadDefaultValues()` method:
```php
$customer = new Customer();
$customer->loadDefaultValues();
$customer->name = 'Alexander';
$customer->save();
```
Data Input and Validation Data Input and Validation
------------------------- -------------------------
......
...@@ -53,7 +53,7 @@ class SiteController extends Controller ...@@ -53,7 +53,7 @@ class SiteController extends Controller
public function actionIndex() public function actionIndex()
{ {
#CSRF validation will no be applied on this and other actions // CSRF validation will not be applied to this and other actions
} }
} }
......
...@@ -122,7 +122,7 @@ an [FPM SAPI](http://php.net/install.fpm). Below is a sample host configuration ...@@ -122,7 +122,7 @@ an [FPM SAPI](http://php.net/install.fpm). Below is a sample host configuration
The configuration tells the server to send all requests for non-existent resources through the bootstrap file, The configuration tells the server to send all requests for non-existent resources through the bootstrap file,
resulting in "prettier" URLs without the need for `index.php` references. resulting in "prettier" URLs without the need for `index.php` references.
~~~ ```
server { server {
set $yii_bootstrap "index.php"; set $yii_bootstrap "index.php";
charset utf-8; charset utf-8;
...@@ -159,6 +159,10 @@ server { ...@@ -159,6 +159,10 @@ server {
deny all; deny all;
} }
} }
~~~ ```
When using this configuration, you should set `cgi.fix_pathinfo=0` in the `php.ini` file in order to avoid many unnecessary system `stat()` calls. When using this configuration, you should set `cgi.fix_pathinfo=0` in the `php.ini` file in order to avoid many unnecessary system `stat()` calls.
Note that when running a HTTPS server you need to add `fastcgi_param HTTPS on;` in order for Yii to properly detect if
connection is secure.
...@@ -12,11 +12,11 @@ Yii Framework 2 bootstrap extension Change Log ...@@ -12,11 +12,11 @@ Yii Framework 2 bootstrap extension Change Log
- Enh #1601: Added support for tagName and encodeLabel parameters in ButtonDropdown (omnilight) - Enh #1601: Added support for tagName and encodeLabel parameters in ButtonDropdown (omnilight)
- Enh #1881: Improved `yii\bootstrap\NavBar` with `containerOptions`, `innerContainerOptions` and `renderInnerContainer` (creocoder) - Enh #1881: Improved `yii\bootstrap\NavBar` with `containerOptions`, `innerContainerOptions` and `renderInnerContainer` (creocoder)
- Enh #2425: Tabs widget now selects first tab if no active tab is specified (samdark) - Enh #2425: Tabs widget now selects first tab if no active tab is specified (samdark)
- Enh #2634: Submenus will now be checked for being active (Alex-Code)
- Enh #2643: Add size attribute to Modal (tof06) - Enh #2643: Add size attribute to Modal (tof06)
- Chg #1459: Update Collapse to use bootstrap 3 classes (tonydspaniard) - Chg #1459: Update Collapse to use bootstrap 3 classes (tonydspaniard)
- Chg #1820: Update Progress to use bootstrap 3 markup (samdark) - Chg #1820: Update Progress to use bootstrap 3 markup (samdark)
2.0.0 alpha, December 1, 2013 2.0.0 alpha, December 1, 2013
----------------------------- -----------------------------
......
...@@ -75,6 +75,10 @@ class Nav extends Widget ...@@ -75,6 +75,10 @@ class Nav extends Widget
*/ */
public $activateItems = true; public $activateItems = true;
/** /**
* @var boolean whether to activate parent menu items when one of the corresponding child menu items is active.
*/
public $activateParents = false;
/**
* @var string the route used to determine if a menu item is active or not. * @var string the route used to determine if a menu item is active or not.
* If not set, it will use the route of the current request. * If not set, it will use the route of the current request.
* @see params * @see params
...@@ -156,16 +160,15 @@ class Nav extends Widget ...@@ -156,16 +160,15 @@ class Nav extends Widget
$active = $this->isItemActive($item); $active = $this->isItemActive($item);
} }
if ($active) {
Html::addCssClass($options, 'active');
}
if ($items !== null) { if ($items !== null) {
$linkOptions['data-toggle'] = 'dropdown'; $linkOptions['data-toggle'] = 'dropdown';
Html::addCssClass($options, 'dropdown'); Html::addCssClass($options, 'dropdown');
Html::addCssClass($linkOptions, 'dropdown-toggle'); Html::addCssClass($linkOptions, 'dropdown-toggle');
$label .= ' ' . Html::tag('b', '', ['class' => 'caret']); $label .= ' ' . Html::tag('b', '', ['class' => 'caret']);
if (is_array($items)) { if (is_array($items)) {
if ($this->activateItems) {
$items = $this->isChildActive($items, $active);
}
$items = Dropdown::widget([ $items = Dropdown::widget([
'items' => $items, 'items' => $items,
'encodeLabels' => $this->encodeLabels, 'encodeLabels' => $this->encodeLabels,
...@@ -175,9 +178,31 @@ class Nav extends Widget ...@@ -175,9 +178,31 @@ class Nav extends Widget
} }
} }
if ($this->activateItems && $active) {
Html::addCssClass($options, 'active');
}
return Html::tag('li', Html::a($label, $url, $linkOptions) . $items, $options); return Html::tag('li', Html::a($label, $url, $linkOptions) . $items, $options);
} }
/**
* Check to see if a child item is active optionally activating the parent.
* @param array $items @see items
* @param boolean $active should the parent be active too
* @return array @see items
*/
protected function isChildActive($items, &$active)
{
foreach ($items as $i => $child) {
if (ArrayHelper::remove($items[$i], 'active', false) || $this->isItemActive($child)) {
Html::addCssClass($items[$i]['options'], 'active');
if ($this->activateParents) {
$active = true;
}
}
}
return $items;
}
/** /**
* Checks whether a menu item is active. * Checks whether a menu item is active.
......
...@@ -47,6 +47,15 @@ abstract class Generator extends Model ...@@ -47,6 +47,15 @@ abstract class Generator extends Model
* The value of this property is internally managed by this class. * The value of this property is internally managed by this class.
*/ */
public $template; public $template;
/**
* @var boolean whether the strings will be generated using `Yii::t()` or normal strings.
*/
public $enableI18N = false;
/**
* @var string the message category used by `Yii::t()` when `$enableI18N` is `true`.
* Defaults to `app`.
*/
public $messageCategory = 'app';
/** /**
* @return string name of the code generator * @return string name of the code generator
...@@ -76,6 +85,17 @@ abstract class Generator extends Model ...@@ -76,6 +85,17 @@ abstract class Generator extends Model
} }
/** /**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'enableI18N' => 'Enable I18N',
'messageCategory' => 'Message Category',
];
}
/**
* Returns a list of code template files that are required. * Returns a list of code template files that are required.
* Derived classes usually should override this method if they require the existence of * Derived classes usually should override this method if they require the existence of
* certain template files. * certain template files.
...@@ -95,7 +115,7 @@ abstract class Generator extends Model ...@@ -95,7 +115,7 @@ abstract class Generator extends Model
*/ */
public function stickyAttributes() public function stickyAttributes()
{ {
return ['template']; return ['template', 'enableI18N', 'messageCategory'];
} }
/** /**
...@@ -106,7 +126,11 @@ abstract class Generator extends Model ...@@ -106,7 +126,11 @@ abstract class Generator extends Model
*/ */
public function hints() public function hints()
{ {
return []; return [
'enableI18N' => 'This indicates whether the generator should generate strings using <code>Yii::t()</code> method.
Set this to <code>true</code> if you are planning to make your application translatable.',
'messageCategory' => 'This is the category used by <code>Yii::t()</code> in case you enable I18N.',
];
} }
/** /**
...@@ -359,6 +383,16 @@ abstract class Generator extends Model ...@@ -359,6 +383,16 @@ abstract class Generator extends Model
} }
/** /**
* Checks if message category is not empty when I18N is enabled.
*/
public function validateMessageCategory()
{
if ($this->enableI18N && empty($this->messageCategory)) {
$this->addError('messageCategory', "Message Category cannot be blank.");
}
}
/**
* @param string $value the attribute to be validated * @param string $value the attribute to be validated
* @return boolean whether the value is a reserved PHP keyword. * @return boolean whether the value is a reserved PHP keyword.
*/ */
...@@ -448,4 +482,38 @@ abstract class Generator extends Model ...@@ -448,4 +482,38 @@ abstract class Generator extends Model
return in_array(strtolower($value), $keywords, true); return in_array(strtolower($value), $keywords, true);
} }
/**
* Generates a string depending on enableI18N property
* @param string $string the text be generated
* @param array $placeholders the placeholders to use by `Yii::t()`
*/
public function generateString($string = '', $placeholders = [])
{
$string = addslashes($string);
if ($this->enableI18N) {
// If there are placeholders, use them
if (!empty($placeholders)) {
$search = ['array (', ')'];
$replace = ['[', ']'];
$ph = ', ' . str_replace($search, $replace, var_export($placeholders, true));
} else {
$ph = '';
}
$str = "Yii::t('" . $this->messageCategory . "', '" . $string . "'" . $ph . ")";
} else {
// No I18N, replace placeholders by real words, if any
if (!empty($placeholders)) {
$phKeys = array_map(function($word) {
return '{' . $word . '}';
}, array_keys($placeholders));
$phValues = array_values($placeholders);
$str = "'" . str_replace($phKeys, $phValues, $string) . "'";
} else {
// No placeholders, just the given string
$str = "'" . $string . "'";
}
}
return $str;
}
} }
...@@ -111,10 +111,15 @@ yii.gii = (function ($) { ...@@ -111,10 +111,15 @@ yii.gii = (function ($) {
initConfirmationCheckboxes(); initConfirmationCheckboxes();
// model generator: hide class name input when table name input contains * // model generator: hide class name input when table name input contains *
$('#model-generator #generator-tablename').on('change',function () { $('#model-generator #generator-tablename').change(function () {
$('#model-generator .field-generator-modelclass').toggle($(this).val().indexOf('*') == -1); $('#model-generator .field-generator-modelclass').toggle($(this).val().indexOf('*') == -1);
}).change(); }).change();
// CRUD generator: hide messageCategory when I18N is disabled
$('#crud-generator #generator-enablei18n').change(function () {
$('#crud-generator .field-generator-messagecategory').toggle($(this).is(':checked'));
}).change();
// hide Generate button if any input is changed // hide Generate button if any input is changed
$('.default-view .form-group input,select,textarea').change(function () { $('.default-view .form-group input,select,textarea').change(function () {
$('.default-view-results,.default-view-files').hide(); $('.default-view-results,.default-view-files').hide();
......
...@@ -71,6 +71,8 @@ class Generator extends \yii\gii\Generator ...@@ -71,6 +71,8 @@ class Generator extends \yii\gii\Generator
[['indexWidgetType'], 'in', 'range' => ['grid', 'list']], [['indexWidgetType'], 'in', 'range' => ['grid', 'list']],
[['modelClass'], 'validateModelClass'], [['modelClass'], 'validateModelClass'],
[['moduleID'], 'validateModuleID'], [['moduleID'], 'validateModuleID'],
[['enableI18N'], 'boolean'],
[['messageCategory'], 'validateMessageCategory', 'skipOnEmpty' => false],
]); ]);
} }
...@@ -94,7 +96,7 @@ class Generator extends \yii\gii\Generator ...@@ -94,7 +96,7 @@ class Generator extends \yii\gii\Generator
*/ */
public function hints() public function hints()
{ {
return [ return array_merge(parent::hints(), [
'modelClass' => 'This is the ActiveRecord class associated with the table that CRUD will be built upon. 'modelClass' => 'This is the ActiveRecord class associated with the table that CRUD will be built upon.
You should provide a fully qualified class name, e.g., <code>app\models\Post</code>.', You should provide a fully qualified class name, e.g., <code>app\models\Post</code>.',
'controllerClass' => 'This is the name of the controller class to be generated. You should 'controllerClass' => 'This is the name of the controller class to be generated. You should
...@@ -107,7 +109,7 @@ class Generator extends \yii\gii\Generator ...@@ -107,7 +109,7 @@ class Generator extends \yii\gii\Generator
You may choose either <code>GridView</code> or <code>ListView</code>', You may choose either <code>GridView</code> or <code>ListView</code>',
'searchModelClass' => 'This is the name of the search model class to be generated. You should provide a fully 'searchModelClass' => 'This is the name of the search model class to be generated. You should provide a fully
qualified namespaced class name, e.g., <code>app\models\search\PostSearch</code>.', qualified namespaced class name, e.g., <code>app\models\search\PostSearch</code>.',
]; ]);
} }
/** /**
...@@ -123,7 +125,7 @@ class Generator extends \yii\gii\Generator ...@@ -123,7 +125,7 @@ class Generator extends \yii\gii\Generator
*/ */
public function stickyAttributes() public function stickyAttributes()
{ {
return ['baseControllerClass', 'moduleID', 'indexWidgetType']; return array_merge(parent::stickyAttributes(), ['baseControllerClass', 'moduleID', 'indexWidgetType']);
} }
/** /**
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
use yii\helpers\StringHelper; use yii\helpers\StringHelper;
/** /**
* This is the template for generating a CRUD controller class file. * This is the template for generating CRUD search class of the specified model.
* *
* @var yii\web\View $this * @var yii\web\View $this
* @var yii\gii\generators\crud\Generator $generator * @var yii\gii\generators\crud\Generator $generator
...@@ -49,7 +49,7 @@ class <?= $searchModelClass ?> extends Model ...@@ -49,7 +49,7 @@ class <?= $searchModelClass ?> extends Model
{ {
return [ return [
<?php foreach ($labels as $name => $label): ?> <?php foreach ($labels as $name => $label): ?>
<?= "'$name' => '" . addslashes($label) . "',\n" ?> <?= "'$name' => " . $generator->generateString($label) . ",\n" ?>
<?php endforeach; ?> <?php endforeach; ?>
]; ];
} }
......
...@@ -36,7 +36,7 @@ use yii\widgets\ActiveForm; ...@@ -36,7 +36,7 @@ use yii\widgets\ActiveForm;
echo " <?= " . $generator->generateActiveField($attribute) . " ?>\n\n"; echo " <?= " . $generator->generateActiveField($attribute) . " ?>\n\n";
} ?> } ?>
<div class="form-group"> <div class="form-group">
<?= "<?= " ?>Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?> <?= "<?= " ?>Html::submitButton($model->isNewRecord ? <?= $generator->generateString('Create') ?> : <?= $generator->generateString('Update') ?>, ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div> </div>
<?= "<?php " ?>ActiveForm::end(); ?> <?= "<?php " ?>ActiveForm::end(); ?>
......
...@@ -39,8 +39,8 @@ foreach ($generator->getColumnNames() as $attribute) { ...@@ -39,8 +39,8 @@ foreach ($generator->getColumnNames() as $attribute) {
} }
?> ?>
<div class="form-group"> <div class="form-group">
<?= "<?= " ?>Html::submitButton('Search', ['class' => 'btn btn-primary']) ?> <?= "<?= " ?>Html::submitButton(<?= $generator->generateString('Search') ?>, ['class' => 'btn btn-primary']) ?>
<?= "<?= " ?>Html::resetButton('Reset', ['class' => 'btn btn-default']) ?> <?= "<?= " ?>Html::resetButton(<?= $generator->generateString('Reset') ?>, ['class' => 'btn btn-default']) ?>
</div> </div>
<?= "<?php " ?>ActiveForm::end(); ?> <?= "<?php " ?>ActiveForm::end(); ?>
......
...@@ -18,8 +18,8 @@ use yii\helpers\Html; ...@@ -18,8 +18,8 @@ use yii\helpers\Html;
* @var <?= ltrim($generator->modelClass, '\\') ?> $model * @var <?= ltrim($generator->modelClass, '\\') ?> $model
*/ */
$this->title = 'Create <?= Inflector::camel2words(StringHelper::basename($generator->modelClass)) ?>'; $this->title = <?= $generator->generateString('Create {modelClass}', ['modelClass' => Inflector::camel2words(StringHelper::basename($generator->modelClass))]) ?>;
$this->params['breadcrumbs'][] = ['label' => '<?= Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass))) ?>', 'url' => ['index']]; $this->params['breadcrumbs'][] = ['label' => <?= $generator->generateString(Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass)))) ?>, 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title; $this->params['breadcrumbs'][] = $this->title;
?> ?>
<div class="<?= Inflector::camel2id(StringHelper::basename($generator->modelClass)) ?>-create"> <div class="<?= Inflector::camel2id(StringHelper::basename($generator->modelClass)) ?>-create">
......
...@@ -23,7 +23,7 @@ use <?= $generator->indexWidgetType === 'grid' ? "yii\\grid\\GridView" : "yii\\w ...@@ -23,7 +23,7 @@ use <?= $generator->indexWidgetType === 'grid' ? "yii\\grid\\GridView" : "yii\\w
* @var <?= ltrim($generator->searchModelClass, '\\') ?> $searchModel * @var <?= ltrim($generator->searchModelClass, '\\') ?> $searchModel
*/ */
$this->title = '<?= Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass))) ?>'; $this->title = <?= $generator->generateString(Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass)))) ?>;
$this->params['breadcrumbs'][] = $this->title; $this->params['breadcrumbs'][] = $this->title;
?> ?>
<div class="<?= Inflector::camel2id(StringHelper::basename($generator->modelClass)) ?>-index"> <div class="<?= Inflector::camel2id(StringHelper::basename($generator->modelClass)) ?>-index">
...@@ -33,7 +33,7 @@ $this->params['breadcrumbs'][] = $this->title; ...@@ -33,7 +33,7 @@ $this->params['breadcrumbs'][] = $this->title;
<?= "<?php " . ($generator->indexWidgetType === 'grid' ? "// " : "") ?>echo $this->render('_search', ['model' => $searchModel]); ?> <?= "<?php " . ($generator->indexWidgetType === 'grid' ? "// " : "") ?>echo $this->render('_search', ['model' => $searchModel]); ?>
<p> <p>
<?= "<?= " ?>Html::a('Create <?= Inflector::camel2words(StringHelper::basename($generator->modelClass)) ?>', ['create'], ['class' => 'btn btn-success']) ?> <?= "<?= " ?>Html::a(<?= $generator->generateString('Create {modelClass}', ['modelClass' => Inflector::camel2words(StringHelper::basename($generator->modelClass))]) ?>, ['create'], ['class' => 'btn btn-success']) ?>
</p> </p>
<?php if ($generator->indexWidgetType === 'grid'): ?> <?php if ($generator->indexWidgetType === 'grid'): ?>
......
...@@ -20,10 +20,10 @@ use yii\helpers\Html; ...@@ -20,10 +20,10 @@ use yii\helpers\Html;
* @var <?= ltrim($generator->modelClass, '\\') ?> $model * @var <?= ltrim($generator->modelClass, '\\') ?> $model
*/ */
$this->title = 'Update <?= Inflector::camel2words(StringHelper::basename($generator->modelClass)) ?>: ' . $model-><?= $generator->getNameAttribute() ?>; $this->title = <?= $generator->generateString('Update {modelClass}: ', ['modelClass' => Inflector::camel2words(StringHelper::basename($generator->modelClass))]) ?> . $model-><?= $generator->getNameAttribute() ?>;
$this->params['breadcrumbs'][] = ['label' => '<?= Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass))) ?>', 'url' => ['index']]; $this->params['breadcrumbs'][] = ['label' => <?= $generator->generateString(Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass)))) ?>, 'url' => ['index']];
$this->params['breadcrumbs'][] = ['label' => $model-><?= $generator->getNameAttribute() ?>, 'url' => ['view', <?= $urlParams ?>]]; $this->params['breadcrumbs'][] = ['label' => $model-><?= $generator->getNameAttribute() ?>, 'url' => ['view', <?= $urlParams ?>]];
$this->params['breadcrumbs'][] = 'Update'; $this->params['breadcrumbs'][] = <?= $generator->generateString('Update') ?>;
?> ?>
<div class="<?= Inflector::camel2id(StringHelper::basename($generator->modelClass)) ?>-update"> <div class="<?= Inflector::camel2id(StringHelper::basename($generator->modelClass)) ?>-update">
......
...@@ -22,7 +22,7 @@ use yii\widgets\DetailView; ...@@ -22,7 +22,7 @@ use yii\widgets\DetailView;
*/ */
$this->title = $model-><?= $generator->getNameAttribute() ?>; $this->title = $model-><?= $generator->getNameAttribute() ?>;
$this->params['breadcrumbs'][] = ['label' => '<?= Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass))) ?>', 'url' => ['index']]; $this->params['breadcrumbs'][] = ['label' => <?= $generator->generateString(Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass)))) ?>, 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title; $this->params['breadcrumbs'][] = $this->title;
?> ?>
<div class="<?= Inflector::camel2id(StringHelper::basename($generator->modelClass)) ?>-view"> <div class="<?= Inflector::camel2id(StringHelper::basename($generator->modelClass)) ?>-view">
...@@ -30,11 +30,11 @@ $this->params['breadcrumbs'][] = $this->title; ...@@ -30,11 +30,11 @@ $this->params['breadcrumbs'][] = $this->title;
<h1><?= "<?= " ?>Html::encode($this->title) ?></h1> <h1><?= "<?= " ?>Html::encode($this->title) ?></h1>
<p> <p>
<?= "<?= " ?>Html::a('Update', ['update', <?= $urlParams ?>], ['class' => 'btn btn-primary']) ?> <?= "<?= " ?>Html::a(<?= $generator->generateString('Update') ?>, ['update', <?= $urlParams ?>], ['class' => 'btn btn-primary']) ?>
<?= "<?= " ?>Html::a('Delete', ['delete', <?= $urlParams ?>], [ <?= "<?= " ?>Html::a(<?= $generator->generateString('Delete') ?>, ['delete', <?= $urlParams ?>], [
'class' => 'btn btn-danger', 'class' => 'btn btn-danger',
'data' => [ 'data' => [
'confirm' => Yii::t('app', 'Are you sure you want to delete this item?'), 'confirm' => <?= $generator->generateString('Are you sure you want to delete this item?') ?>,
'method' => 'post', 'method' => 'post',
], ],
]) ?> ]) ?>
......
...@@ -14,3 +14,5 @@ echo $form->field($generator, 'indexWidgetType')->dropDownList([ ...@@ -14,3 +14,5 @@ echo $form->field($generator, 'indexWidgetType')->dropDownList([
'grid' => 'GridView', 'grid' => 'GridView',
'list' => 'ListView', 'list' => 'ListView',
]); ]);
echo $form->field($generator, 'enableI18N')->checkbox();
echo $form->field($generator, 'messageCategory');
...@@ -60,6 +60,7 @@ Yii Framework 2 Change Log ...@@ -60,6 +60,7 @@ Yii Framework 2 Change Log
- Bug #2739: Fixed the issue that `CreateAction::run()` was using obsolete `Controller::createAbsoluteUrl()` method (tonydspaniard) - Bug #2739: Fixed the issue that `CreateAction::run()` was using obsolete `Controller::createAbsoluteUrl()` method (tonydspaniard)
- Bug #2740: Fixed the issue that `CaptchaAction::run()` was using obsolete `Controller::createUrl()` method (tonydspaniard) - Bug #2740: Fixed the issue that `CaptchaAction::run()` was using obsolete `Controller::createUrl()` method (tonydspaniard)
- Bug #2760: Fixed GridView `filterUrl` parameters (qiangxue, AlexGx) - Bug #2760: Fixed GridView `filterUrl` parameters (qiangxue, AlexGx)
- Bug #2834: When overriding i18n translation sources from config using `app*` or `yii*` default `app` and `yii` sources were not removed (samdark)
- Bug: Fixed `Call to a member function registerAssetFiles() on a non-object` in case of wrong `sourcePath` for an asset bundle (samdark) - Bug: Fixed `Call to a member function registerAssetFiles() on a non-object` in case of wrong `sourcePath` for an asset bundle (samdark)
- Bug: Fixed incorrect event name for `yii\jui\Spinner` (samdark) - Bug: Fixed incorrect event name for `yii\jui\Spinner` (samdark)
- Bug: Json::encode() did not handle objects that implement JsonSerializable interface correctly (cebe) - Bug: Json::encode() did not handle objects that implement JsonSerializable interface correctly (cebe)
...@@ -145,6 +146,8 @@ Yii Framework 2 Change Log ...@@ -145,6 +146,8 @@ Yii Framework 2 Change Log
- Enh #2646: Added support for specifying hostinfo in the pattern of a URL rule (qiangxue) - Enh #2646: Added support for specifying hostinfo in the pattern of a URL rule (qiangxue)
- Enh #2661: Added boolean column type support for SQLite (qiangxue) - Enh #2661: Added boolean column type support for SQLite (qiangxue)
- Enh #2670: Changed `console\Controller::globalOptions()` to `options($actionId)` to (make it possible to) differentiate options per action (hqx) - Enh #2670: Changed `console\Controller::globalOptions()` to `options($actionId)` to (make it possible to) differentiate options per action (hqx)
- Enh #2714: Added support for formatting time intervals relative to the current time with `yii\base\Formatter` (drenty)
- Enh #2726: Added `yii\db\ActiveRecord::loadDefaultValues()` that fills default values from DB schema (samdark)
- Enh #2729: Added `FilterValidator::skipOnArray` so that filters like `trim` will not fail for array inputs (qiangxue) - Enh #2729: Added `FilterValidator::skipOnArray` so that filters like `trim` will not fail for array inputs (qiangxue)
- Enh #2735: Added support for `DateTimeInterface` in `Formatter` (ivokund) - Enh #2735: Added support for `DateTimeInterface` in `Formatter` (ivokund)
- Enh #2756: Added support for injecting custom `isEmpty` check for all validators (qiangxue) - Enh #2756: Added support for injecting custom `isEmpty` check for all validators (qiangxue)
......
...@@ -470,4 +470,93 @@ class Formatter extends Component ...@@ -470,4 +470,93 @@ class Formatter extends Component
return $verbose ? Yii::t('yii', '{n, plural, =1{# petabyte} other{# petabytes}}', $params) : Yii::t('yii', '{n} PB', $params); return $verbose ? Yii::t('yii', '{n, plural, =1{# petabyte} other{# petabytes}}', $params) : Yii::t('yii', '{n} PB', $params);
} }
} }
/**
* Formats the value as the time interval between a date and now in human readable form.
* @param integer|string|DateTime|DateInterval $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 into a UNIX timestamp via `strtotime()` or that can be passed to a DateInterval constructor.
* - a PHP DateTime object
* - a PHP DateInterval object (a positive time interval will refer to the past, a negative one to the future)
*
* @return string the formatted result
*/
public function asRelativeTime($value, $referenceTime = null)
{
if ($value === null) {
return $this->nullDisplay;
}
if ($value instanceof \DateInterval) {
$interval = $value;
} else {
$timestamp = $this->normalizeDatetimeValue($value);
if ($timestamp === false) {
// $value is not a valid date/time value, so we try
// to create a DateInterval with it
try {
$interval = new \DateInterval($value);
} catch (Exception $e) {
// invalid date/time and invalid interval
return $this->nullDisplay;
}
} else {
$timezone = new \DateTimeZone($this->timeZone);
if ($referenceTime === null) {
$dateNow = new DateTime('now', $timezone);
} else {
$referenceTime = $this->normalizeDatetimeValue($referenceTime);
$dateNow = new DateTime(null, $timezone);
$dateNow->setTimestamp($referenceTime);
}
$dateThen = new DateTime(null, $timezone);
$dateThen->setTimestamp($timestamp);
$interval = $dateThen->diff($dateNow);
}
}
if ($interval->invert) {
if ($interval->y >= 1) {
return Yii::t('yii', 'in {delta, plural, =1{a year} other{# years}}', ['delta' => $interval->y]);
}
if ($interval->m >= 1) {
return Yii::t('yii', 'in {delta, plural, =1{a month} other{# months}}', ['delta' => $interval->m]);
}
if ($interval->d >= 1) {
return Yii::t('yii', 'in {delta, plural, =1{a day} other{# days}}', ['delta' => $interval->d]);
}
if ($interval->h >= 1) {
return Yii::t('yii', 'in {delta, plural, =1{an hour} other{# hours}}', ['delta' => $interval->h]);
}
if ($interval->i >= 1) {
return Yii::t('yii', 'in {delta, plural, =1{a minute} other{# minutes}}', ['delta' => $interval->i]);
}
return Yii::t('yii', 'in {delta, plural, =1{a second} other{# seconds}}', ['delta' => $interval->s]);
} else {
if ($interval->y >= 1) {
return Yii::t('yii', '{delta, plural, =1{a year} other{# years}} ago', ['delta' => $interval->y]);
}
if ($interval->m >= 1) {
return Yii::t('yii', '{delta, plural, =1{a month} other{# months}} ago', ['delta' => $interval->m]);
}
if ($interval->d >= 1) {
return Yii::t('yii', '{delta, plural, =1{a day} other{# days}} ago', ['delta' => $interval->d]);
}
if ($interval->h >= 1) {
return Yii::t('yii', '{delta, plural, =1{an hour} other{# hours}} ago', ['delta' => $interval->h]);
}
if ($interval->i >= 1) {
return Yii::t('yii', '{delta, plural, =1{a minute} other{# minutes}} ago', ['delta' => $interval->i]);
}
return Yii::t('yii', '{delta, plural, =1{a second} other{# seconds}} ago', ['delta' => $interval->s]);
}
}
} }
...@@ -82,12 +82,11 @@ class Theme extends Component ...@@ -82,12 +82,11 @@ class Theme extends Component
{ {
parent::init(); parent::init();
if (($basePath = $this->getBasePath()) !== null) { if (empty($this->pathMap)) {
if (empty($this->pathMap)) { if (($basePath = $this->getBasePath()) === null) {
$this->pathMap = [Yii::$app->getBasePath() => [$basePath]]; throw new InvalidConfigException('The "basePath" property must be set.');
} }
} else { $this->pathMap = [Yii::$app->getBasePath() => [$basePath]];
throw new InvalidConfigException('The "basePath" property must be set.');
} }
} }
...@@ -173,13 +172,18 @@ class Theme extends Component ...@@ -173,13 +172,18 @@ class Theme extends Component
} }
} }
/** /**
* Converts a relative file path into an absolute one using [[basePath]]. * Converts a relative file path into an absolute one using [[basePath]].
* @param string $path the relative file path to be converted. * @param string $path the relative file path to be converted.
* @return string the absolute file path * @return string the absolute file path
*/ */
public function getPath($path) public function getPath($path)
{ {
return $this->getBasePath() . DIRECTORY_SEPARATOR . ltrim($path, '/\\'); if (($basePath = $this->getBasePath()) !== null) {
return $basePath . DIRECTORY_SEPARATOR . ltrim($path, '/\\');
} else {
throw new InvalidConfigException('The "basePath" property must be set.');
}
} }
} }
...@@ -94,6 +94,22 @@ class ActiveRecord extends BaseActiveRecord ...@@ -94,6 +94,22 @@ class ActiveRecord extends BaseActiveRecord
const OP_ALL = 0x07; const OP_ALL = 0x07;
/** /**
* Loads default values from database table schema
*
* @param boolean $skipIfSet if existing value should be preserved
* @return static model instance
*/
public function loadDefaultValues($skipIfSet = true)
{
foreach ($this->getTableSchema()->columns as $column) {
if ($column->defaultValue && !($skipIfSet && $this->{$column->name} !== null)) {
$this->{$column->name} = $column->defaultValue;
}
}
return $this;
}
/**
* Returns the database connection used by this AR class. * Returns the database connection used by this AR class.
* By default, the "db" application component is used as the database connection. * By default, the "db" application component is used as the database connection.
* You may override this method if you want to use a different database connection. * You may override this method if you want to use a different database connection.
......
...@@ -17,7 +17,8 @@ use yii\base\InvalidCallException; ...@@ -17,7 +17,8 @@ use yii\base\InvalidCallException;
* iterating through the reader. For example, * iterating through the reader. For example,
* *
* ~~~ * ~~~
* $reader = $command->query('SELECT * FROM tbl_post'); * $command = $connection->createCommand('SELECT * FROM tbl_post');
* $reader = $command->query();
* *
* while ($row = $reader->read()) { * while ($row = $reader->read()) {
* $rows[] = $row; * $rows[] = $row;
......
...@@ -20,9 +20,13 @@ class QueryBuilder extends \yii\db\QueryBuilder ...@@ -20,9 +20,13 @@ class QueryBuilder extends \yii\db\QueryBuilder
private $sql; private $sql;
public function build($query) /**
* @inheritdoc
*/
public function build($query, $params = [])
{ {
$params = $query->params; $params = empty($params) ? $query->params : array_merge($params, $query->params);
$clauses = [ $clauses = [
$this->buildSelect($query->select, $params, $query->distinct, $query->selectOption), $this->buildSelect($query->select, $params, $query->distinct, $query->selectOption),
$this->buildFrom($query->from, $params), $this->buildFrom($query->from, $params),
......
...@@ -373,6 +373,12 @@ SQL; ...@@ -373,6 +373,12 @@ SQL;
$table->sequenceName = preg_replace(['/nextval/', '/::/', '/regclass/', '/\'\)/', '/\(\'/'], '', $column->defaultValue); $table->sequenceName = preg_replace(['/nextval/', '/::/', '/regclass/', '/\'\)/', '/\(\'/'], '', $column->defaultValue);
} }
} }
if ($column->defaultValue) {
if (preg_match("/^'(.*?)'::/", $column->defaultValue, $matches) || preg_match("/^(.*?)::/", $column->defaultValue, $matches)) {
$column->defaultValue = $matches[1];
}
}
} }
return true; return true;
...@@ -398,7 +404,6 @@ SQL; ...@@ -398,7 +404,6 @@ SQL;
$column->precision = $info['numeric_precision']; $column->precision = $info['numeric_precision'];
$column->scale = $info['numeric_scale']; $column->scale = $info['numeric_scale'];
$column->size = $info['size']; $column->size = $info['size'];
if (isset($this->typeMap[$column->dbType])) { if (isset($this->typeMap[$column->dbType])) {
$column->type = $this->typeMap[$column->dbType]; $column->type = $this->typeMap[$column->dbType];
} else { } else {
...@@ -406,6 +411,7 @@ SQL; ...@@ -406,6 +411,7 @@ SQL;
} }
$column->phpType = $this->getColumnPhpType($column); $column->phpType = $this->getColumnPhpType($column);
return $column; return $column;
} }
} }
...@@ -238,9 +238,9 @@ class Schema extends \yii\db\Schema ...@@ -238,9 +238,9 @@ class Schema extends \yii\db\Schema
} }
$column->phpType = $this->getColumnPhpType($column); $column->phpType = $this->getColumnPhpType($column);
$value = $info['dflt_value']; $value = trim($info['dflt_value'], "'\"");
if ($column->type === 'string') { if ($column->type === 'string') {
$column->defaultValue = trim($value, "'\""); $column->defaultValue = $value;
} else { } else {
$column->defaultValue = $column->typecast(strcasecmp($value, 'null') ? $value : null); $column->defaultValue = $column->typecast(strcasecmp($value, 'null') ? $value : null);
} }
......
...@@ -54,14 +54,14 @@ class I18N extends Component ...@@ -54,14 +54,14 @@ class I18N extends Component
public function init() public function init()
{ {
parent::init(); parent::init();
if (!isset($this->translations['yii'])) { if (!isset($this->translations['yii']) && !isset($this->translations['yii*'])) {
$this->translations['yii'] = [ $this->translations['yii'] = [
'class' => 'yii\i18n\PhpMessageSource', 'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en', 'sourceLanguage' => 'en',
'basePath' => '@yii/messages', 'basePath' => '@yii/messages',
]; ];
} }
if (!isset($this->translations['app'])) { if (!isset($this->translations['app']) && !isset($this->translations['app*'])) {
$this->translations['app'] = [ $this->translations['app'] = [
'class' => 'yii\i18n\PhpMessageSource', 'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => Yii::$app->sourceLanguage, 'sourceLanguage' => Yii::$app->sourceLanguage,
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
return array ( return array (
'(not set)' => '(ikke defineret)', '(not set)' => '(ikke defineret)',
'An internal server error occurred.' => 'Der opstod en intern server fejl.', 'An internal server error occurred.' => 'Der opstod en intern server fejl.',
'Are you sure to delete this item?' => 'Er du sikker på, at du vil slette dette element?', 'Are you sure you want to delete this item?' => 'Er du sikker på, at du vil slette dette element?',
'Delete' => 'Slet', 'Delete' => 'Slet',
'Error' => 'Fejl', 'Error' => 'Fejl',
'File upload failed.' => 'Upload af fil fejlede.', 'File upload failed.' => 'Upload af fil fejlede.',
...@@ -32,8 +32,8 @@ return array ( ...@@ -32,8 +32,8 @@ return array (
'No help for unknown command "{command}".' => 'Ingen hjælp til ukendt kommando "{command}".', 'No help for unknown command "{command}".' => 'Ingen hjælp til ukendt kommando "{command}".',
'No help for unknown sub-command "{command}".' => 'Ingen hjælp til ukendt under-kommando "{command}".', 'No help for unknown sub-command "{command}".' => 'Ingen hjælp til ukendt under-kommando "{command}".',
'No results found.' => 'Ingen resultater fundet.', 'No results found.' => 'Ingen resultater fundet.',
'Only files with these MIME types are allowed: {mimeTypes}.' => 'Kun filer med følgende MIME-typer er tilladte: {mimeTypes}.',
'Only files with these extensions are allowed: {extensions}.' => 'Kun filer med følgende filtyper er tilladte: {extensions}.', 'Only files with these extensions are allowed: {extensions}.' => 'Kun filer med følgende filtyper er tilladte: {extensions}.',
'Only files with these mimeTypes are allowed: {mimeTypes}.' => 'Kun filer med følgende mime-typer er tilladte: {mimeTypes}.',
'Page not found.' => 'Siden blev ikke fundet.', 'Page not found.' => 'Siden blev ikke fundet.',
'Please fix the following errors:' => 'Ret venligst følgende fejl:', 'Please fix the following errors:' => 'Ret venligst følgende fejl:',
'Please upload a file.' => 'Venligst upload en fil.', 'Please upload a file.' => 'Venligst upload en fil.',
...@@ -56,6 +56,12 @@ return array ( ...@@ -56,6 +56,12 @@ return array (
'Yes' => 'Ja', 'Yes' => 'Ja',
'You are not allowed to perform this action.' => 'Du har ikke tilladelse til at udføre denne handling.', 'You are not allowed to perform this action.' => 'Du har ikke tilladelse til at udføre denne handling.',
'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Du kan højst uploade {limit, number} {limit, plural, one{fil} other{filer}}.', 'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Du kan højst uploade {limit, number} {limit, plural, one{fil} other{filer}}.',
'in {delta, plural, =1{a day} other{# days}}' => 'om {delta, plural, =1{en dag} other{# dage}}',
'in {delta, plural, =1{a minute} other{# minutes}}' => 'om {delta, plural, =1{et minut} other{# minutter}}',
'in {delta, plural, =1{a month} other{# months}}' => 'om {delta, plural, =1{en måned} other{# måneder}}',
'in {delta, plural, =1{a second} other{# seconds}}' => 'om {delta, plural, =1{et sekund} other{# sekunder}}',
'in {delta, plural, =1{a year} other{# years}}' => 'om {delta, plural, =1{et år} other{# år}}',
'in {delta, plural, =1{an hour} other{# hours}}' => 'om {delta, plural, =1{en time} other{# timer}}',
'the input value' => 'inputværdien', 'the input value' => 'inputværdien',
'{attribute} "{value}" has already been taken.' => '{attribute} "{value}" er allerede i brug.', '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" er allerede i brug.',
'{attribute} cannot be blank.' => '{attribute} må ikke være tom.', '{attribute} cannot be blank.' => '{attribute} må ikke være tom.',
...@@ -78,4 +84,22 @@ return array ( ...@@ -78,4 +84,22 @@ return array (
'{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} skal mindst indeholde {min, number} {min, plural, one{tegn} other{tegn}}.', '{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => '{attribute} skal mindst indeholde {min, number} {min, plural, one{tegn} other{tegn}}.',
'{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} skal højst indeholde {min, number} {min, plural, one{tegn} other{tegn}}.', '{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => '{attribute} skal højst indeholde {min, number} {min, plural, one{tegn} other{tegn}}.',
'{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} skal indeholde {length, number} {length, plural, one{tegn} other{tegn}}.', '{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => '{attribute} skal indeholde {length, number} {length, plural, one{tegn} other{tegn}}.',
'{delta, plural, =1{a day} other{# days}} ago' => 'for {delta, plural, =1{en dag} other{# dage}} siden',
'{delta, plural, =1{a minute} other{# minutes}} ago' => 'for {delta, plural, =1{et minut} other{# minutter}} siden',
'{delta, plural, =1{a month} other{# months}} ago' => 'for {delta, plural, =1{en måned} other{# måneder}} siden',
'{delta, plural, =1{a second} other{# seconds}} ago' => 'for {delta, plural, =1{et sekund} other{# sekunder}} siden',
'{delta, plural, =1{a year} other{# years}} ago' => 'for {delta, plural, =1{et år} other{# år}} siden',
'{delta, plural, =1{an hour} other{# hours}} ago' => 'for {delta, plural, =1{en time} other{# timer}} siden',
'{n, plural, =1{# byte} other{# bytes}}' => '{n, plural, =1{# byte} other{# bytes}}',
'{n, plural, =1{# gigabyte} other{# gigabytes}}' => '{n, plural, =1{# gigabyte} other{# gigabytes}}',
'{n, plural, =1{# kilobyte} other{# kilobytes}}' => '{n, plural, =1{# kilobyte} other{# kilobytes}}',
'{n, plural, =1{# megabyte} other{# megabytes}}' => '{n, plural, =1{# megabyte} other{# megabytes}}',
'{n, plural, =1{# petabyte} other{# petabytes}}' => '{n, plural, =1{# petabyte} other{# petabytes}}',
'{n, plural, =1{# terabyte} other{# terabytes}}' => '{n, plural, =1{# terabyte} other{# terabytes}}',
'{n} B' => '{n} B',
'{n} GB' => '{n} GB',
'{n} KB' => '{n} KB',
'{n} MB' => '{n} MB',
'{n} PB' => '{n} PB',
'{n} TB' => '{n} TB',
); );
...@@ -17,10 +17,21 @@ ...@@ -17,10 +17,21 @@
* NOTE: this file must be saved in UTF-8 encoding. * NOTE: this file must be saved in UTF-8 encoding.
*/ */
return array ( return array (
'the input value' => 'der eingegebene Wert', '{n, plural, =1{# byte} other{# bytes}}' => '{n} Byte',
'{n, plural, =1{# gigabyte} other{# gigabytes}}' => '{n} Gigabyte',
'{n, plural, =1{# kilobyte} other{# kilobytes}}' => '{n} Kilobyte',
'{n, plural, =1{# megabyte} other{# megabytes}}' => '{n} Megabyte',
'{n, plural, =1{# petabyte} other{# petabytes}}' => '{n} Petabyte',
'{n, plural, =1{# terabyte} other{# terabytes}}' => '{n} Terabyte',
'{n} B' => '{n} B',
'{n} GB' => '{n} GB',
'{n} KB' => '{n} KB',
'{n} MB' => '{n} MB',
'{n} PB' => '{n} PB',
'{n} TB' => '{n} TB',
'(not set)' => '(nicht gesetzt)', '(not set)' => '(nicht gesetzt)',
'An internal server error occurred.' => 'Es ist ein interner Serverfehler aufgetreten.', 'An internal server error occurred.' => 'Es ist ein interner Serverfehler aufgetreten.',
'Are you sure to delete this item?' => 'Wollen Sie den Eintrag wirklich löschen?', 'Are you sure you want to delete this item?' => 'Wollen Sie den Eintrag wirklich löschen?',
'Delete' => 'Löschen', 'Delete' => 'Löschen',
'Error' => 'Fehler', 'Error' => 'Fehler',
'File upload failed.' => 'Das Hochladen der Datei ist gescheitert.', 'File upload failed.' => 'Das Hochladen der Datei ist gescheitert.',
...@@ -34,7 +45,7 @@ return array ( ...@@ -34,7 +45,7 @@ return array (
'No help for unknown sub-command "{command}".' => 'Es gibt keine Hilfe für den unbekannten Unterbefehl "{command}".', 'No help for unknown sub-command "{command}".' => 'Es gibt keine Hilfe für den unbekannten Unterbefehl "{command}".',
'No results found.' => 'Keine Ergebnisse gefunden', 'No results found.' => 'Keine Ergebnisse gefunden',
'Only files with these extensions are allowed: {extensions}.' => 'Es sind nur Dateien mit folgenden Dateierweiterungen erlaubt: {extensions}.', 'Only files with these extensions are allowed: {extensions}.' => 'Es sind nur Dateien mit folgenden Dateierweiterungen erlaubt: {extensions}.',
'Only files with these mimeTypes are allowed: {mimeTypes}.' => 'Es sind nur Dateien mit folgenden MIME-Typen erlaubt: {mimeTypes}.', 'Only files with these MIME types are allowed: {mimeTypes}.' => 'Es sind nur Dateien mit folgenden MIME-Typen erlaubt: {mimeTypes}.',
'Page not found.' => 'Seite nicht gefunden.', 'Page not found.' => 'Seite nicht gefunden.',
'Please fix the following errors:' => 'Bitte korrigieren Sie die folgenden Fehler:', 'Please fix the following errors:' => 'Bitte korrigieren Sie die folgenden Fehler:',
'Please upload a file.' => 'Bitte laden Sie eine Datei hoch.', 'Please upload a file.' => 'Bitte laden Sie eine Datei hoch.',
...@@ -57,6 +68,7 @@ return array ( ...@@ -57,6 +68,7 @@ return array (
'Yes' => 'Ja', 'Yes' => 'Ja',
'You are not allowed to perform this action.' => 'Sie dürfen diese Aktion nicht durchführen.', 'You are not allowed to perform this action.' => 'Sie dürfen diese Aktion nicht durchführen.',
'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Sie können maximal {limit, number} {limit, plural, one{eine Datei} other{# Dateien}} hochladen.', 'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Sie können maximal {limit, number} {limit, plural, one{eine Datei} other{# Dateien}} hochladen.',
'the input value' => 'der eingegebene Wert',
'{attribute} "{value}" has already been taken.' => '{attribute} "{value}" wird bereits verwendet.', '{attribute} "{value}" has already been taken.' => '{attribute} "{value}" wird bereits verwendet.',
'{attribute} cannot be blank.' => '{attribute} darf nicht leer sein.', '{attribute} cannot be blank.' => '{attribute} darf nicht leer sein.',
'{attribute} is invalid.' => '{attribute} ist ungültig.', '{attribute} is invalid.' => '{attribute} ist ungültig.',
......
...@@ -17,9 +17,36 @@ ...@@ -17,9 +17,36 @@
* NOTE: this file must be saved in UTF-8 encoding. * NOTE: this file must be saved in UTF-8 encoding.
*/ */
return array ( return array (
'Are you sure you want to delete this item?' => '¿Está seguro de eliminar este elemento?',
'Only files with these MIME types are allowed: {mimeTypes}.' => 'Sólo se aceptan archivos con los siguientes tipos MIME: {mimeTypes}.',
'in {delta, plural, =1{a day} other{# days}}' => 'en {delta, plural, =1{un día} other{# días}}',
'in {delta, plural, =1{a minute} other{# minutes}}' => 'en {delta, plural, =1{un minuto} other{# minutos}}',
'in {delta, plural, =1{a month} other{# months}}' => 'en {delta, plural, =1{un mes} other{# meses}}',
'in {delta, plural, =1{a second} other{# seconds}}' => 'en {delta, plural, =1{un segundo} other{# segundos}}',
'in {delta, plural, =1{a year} other{# years}}' => 'en {delta, plural, =1{un año} other{# años}}',
'in {delta, plural, =1{an hour} other{# hours}}' => 'en {delta, plural, =1{una hora} other{# horas}}',
'{delta, plural, =1{a day} other{# days}} ago' => 'hace {delta, plural, =1{un día} other{# días}}',
'{delta, plural, =1{a minute} other{# minutes}} ago' => 'hace {delta, plural, =1{un minuto} other{# minutos}}',
'{delta, plural, =1{a month} other{# months}} ago' => 'hace {delta, plural, =1{un mes} other{# meses}}',
'{delta, plural, =1{a second} other{# seconds}} ago' => 'hace {delta, plural, =1{un segundo} other{# segundos}}',
'{delta, plural, =1{a year} other{# years}} ago' => 'hace {delta, plural, =1{un año} other{# años}}',
'{delta, plural, =1{an hour} other{# hours}} ago' => 'hace {delta, plural, =1{una hora} other{# horas}}',
'{n, plural, =1{# byte} other{# bytes}}' => '{n, plural, =1{# byte} other{# bytes}}',
'{n, plural, =1{# gigabyte} other{# gigabytes}}' => '{n, plural, =1{# gigabyte} other{# gigabytes}}',
'{n, plural, =1{# kilobyte} other{# kilobytes}}' => '{n, plural, =1{# kilobyte} other{# kilobytes}}',
'{n, plural, =1{# megabyte} other{# megabytes}}' => '{n, plural, =1{# megabyte} other{# megabytes}}',
'{n, plural, =1{# petabyte} other{# petabytes}}' => '{n, plural, =1{# petabyte} other{# petabytes}}',
'{n, plural, =1{# terabyte} other{# terabytes}}' => '{n, plural, =1{# terabyte} other{# terabytes}}',
'{n} B' => '{n} B',
'{n} GB' => '{n} GB',
'{n} KB' => '{n} KB',
'{n} MB' => '{n} MB',
'{n} PB' => '{n} PB',
'{n} TB' => '{n} TB',
'Are you sure to delete this item?' => '@@¿Está seguro de eliminar este elemento?@@',
'Only files with these mimeTypes are allowed: {mimeTypes}.' => '@@Sólo se aceptan archivos con los siguientes tipos mime: {mimeTypes}@@',
'(not set)' => '(no definido)', '(not set)' => '(no definido)',
'An internal server error occurred.' => 'Hubo un error interno del servidor.', 'An internal server error occurred.' => 'Hubo un error interno del servidor.',
'Are you sure to delete this item?' => '¿Está seguro de eliminar este elemento?',
'Delete' => 'Eliminar', 'Delete' => 'Eliminar',
'Error' => 'Error', 'Error' => 'Error',
'File upload failed.' => 'Falló la subida del archivo.', 'File upload failed.' => 'Falló la subida del archivo.',
...@@ -33,7 +60,6 @@ return array ( ...@@ -33,7 +60,6 @@ return array (
'No help for unknown sub-command "{command}".' => 'No existe ayuda para el sub-comando desconocido "{command}"', 'No help for unknown sub-command "{command}".' => 'No existe ayuda para el sub-comando desconocido "{command}"',
'No results found.' => 'No se encontraron resultados.', 'No results found.' => 'No se encontraron resultados.',
'Only files with these extensions are allowed: {extensions}.' => 'Sólo se aceptan archivos con las siguientes extensiones: {extensions}', 'Only files with these extensions are allowed: {extensions}.' => 'Sólo se aceptan archivos con las siguientes extensiones: {extensions}',
'Only files with these mimeTypes are allowed: {mimeTypes}.' => 'Sólo se aceptan archivos con los siguientes tipos mime: {mimeTypes}',
'Page not found.' => 'Página no entontrada.', 'Page not found.' => 'Página no entontrada.',
'Please fix the following errors:' => 'Por favor corrija los siguientes errores:', 'Please fix the following errors:' => 'Por favor corrija los siguientes errores:',
'Please upload a file.' => 'Por favor suba un archivo.', 'Please upload a file.' => 'Por favor suba un archivo.',
......
...@@ -17,9 +17,36 @@ ...@@ -17,9 +17,36 @@
* NOTE: this file must be saved in UTF-8 encoding. * NOTE: this file must be saved in UTF-8 encoding.
*/ */
return array ( return array (
'Are you sure you want to delete this item?' => 'Êtes-vous sûr de vouloir supprimer cet élément ?',
'Only files with these MIME types are allowed: {mimeTypes}.' => 'Seulement les fichiers ayant ces types MIME sont autorisés : {mimeTypes}.',
'in {delta, plural, =1{a day} other{# days}}' => 'dans {delta, plural, =1{un jour} other{# jours}}',
'in {delta, plural, =1{a minute} other{# minutes}}' => 'dans {delta, plural, =1{une minute} other{# minutes}}',
'in {delta, plural, =1{a month} other{# months}}' => 'dans {delta, plural, =1{un mois} other{# mois}}',
'in {delta, plural, =1{a second} other{# seconds}}' => 'dans {delta, plural, =1{une seconde} other{# secondes}}',
'in {delta, plural, =1{a year} other{# years}}' => 'dans {delta, plural, =1{un an} other{# ans}}',
'in {delta, plural, =1{an hour} other{# hours}}' => 'dans {delta, plural, =1{une heure} other{# heures}}',
'{delta, plural, =1{a day} other{# days}} ago' => 'il y a {delta, plural, =1{un jour} other{# jours}}',
'{delta, plural, =1{a minute} other{# minutes}} ago' => 'il y a {delta, plural, =1{une minute} other{# minutes}}',
'{delta, plural, =1{a month} other{# months}} ago' => 'il y a {delta, plural, =1{un mois} other{# mois}}',
'{delta, plural, =1{a second} other{# seconds}} ago' => 'il y a {delta, plural, =1{une seconde} other{# secondes}}',
'{delta, plural, =1{a year} other{# years}} ago' => 'il y a {delta, plural, =1{un an} other{# ans}}',
'{delta, plural, =1{an hour} other{# hours}} ago' => 'il y a {delta, plural, =1{une heure} other{# heures}}',
'{n, plural, =1{# byte} other{# bytes}}' => '{n, plural, =1{# octet} other{# octets}}',
'{n, plural, =1{# gigabyte} other{# gigabytes}}' => '{n, plural, =1{# gigaoctet} other{# gigaoctets}}',
'{n, plural, =1{# kilobyte} other{# kilobytes}}' => '{n, plural, =1{# kilooctet} other{# kilooctets}}',
'{n, plural, =1{# megabyte} other{# megabytes}}' => '{n, plural, =1{# megaoctet} other{# megaoctets}}',
'{n, plural, =1{# petabyte} other{# petabytes}}' => '{n, plural, =1{# petaoctet} other{# petaoctets}}',
'{n, plural, =1{# terabyte} other{# terabytes}}' => '{n, plural, =1{# teraoctet} other{# teraoctets}}',
'{n} B' => '{n} o',
'{n} GB' => '{n} Go',
'{n} KB' => '{n} Ko',
'{n} MB' => '{n} Mo',
'{n} PB' => '{n} Po',
'{n} TB' => '{n} To',
'Are you sure to delete this item?' => '@@Voulez-vous vraiment supprimer cet élément ?@@',
'Only files with these mimeTypes are allowed: {mimeTypes}.' => '@@Les types MIME de fichier autorisés sont : {mimeTypes}.@@',
'(not set)' => '(non défini)', '(not set)' => '(non défini)',
'An internal server error occurred.' => 'Une erreur de serveur interne s\'est produite.', 'An internal server error occurred.' => 'Une erreur de serveur interne s\'est produite.',
'Are you sure to delete this item?' => 'Voulez-vous vraiment supprimer cet élément ?',
'Delete' => 'Supprimer', 'Delete' => 'Supprimer',
'Error' => 'Erreur', 'Error' => 'Erreur',
'File upload failed.' => 'Le téléchargement du fichier a échoué.', 'File upload failed.' => 'Le téléchargement du fichier a échoué.',
...@@ -33,7 +60,6 @@ return array ( ...@@ -33,7 +60,6 @@ return array (
'No help for unknown sub-command "{command}".' => 'Aucune aide pour la sous-commande inconnue « {command} ».', 'No help for unknown sub-command "{command}".' => 'Aucune aide pour la sous-commande inconnue « {command} ».',
'No results found.' => 'Aucun résultat trouvé.', 'No results found.' => 'Aucun résultat trouvé.',
'Only files with these extensions are allowed: {extensions}.' => 'Les extensions de fichier autorisées sont : {extensions}.', 'Only files with these extensions are allowed: {extensions}.' => 'Les extensions de fichier autorisées sont : {extensions}.',
'Only files with these mimeTypes are allowed: {mimeTypes}.' => 'Les types MIME de fichier autorisés sont : {mimeTypes}.',
'Page not found.' => 'Page non trouvée.', 'Page not found.' => 'Page non trouvée.',
'Please fix the following errors:' => 'Veuillez vérifier les erreurs suivantes :', 'Please fix the following errors:' => 'Veuillez vérifier les erreurs suivantes :',
'Please upload a file.' => 'Veuillez télécharger un fichier.', 'Please upload a file.' => 'Veuillez télécharger un fichier.',
......
...@@ -20,7 +20,7 @@ return array ( ...@@ -20,7 +20,7 @@ return array (
'View' => 'Просмотр', 'View' => 'Просмотр',
'(not set)' => '(не задано)', '(not set)' => '(не задано)',
'An internal server error occurred.' => 'Возникла внутренняя ошибка сервера.', 'An internal server error occurred.' => 'Возникла внутренняя ошибка сервера.',
'Are you sure to delete this item?' => 'Вы уверены, что хотите удалить этот элемент?', 'Are you sure you want to delete this item?' => 'Вы уверены, что хотите удалить этот элемент?',
'Delete' => 'Удалить', 'Delete' => 'Удалить',
'Error' => 'Ошибка', 'Error' => 'Ошибка',
'File upload failed.' => 'Загрузка файла не удалась.', 'File upload failed.' => 'Загрузка файла не удалась.',
......
...@@ -20,7 +20,7 @@ return array ( ...@@ -20,7 +20,7 @@ return array (
'View' => 'Перегляд', 'View' => 'Перегляд',
'(not set)' => '(не задано)', '(not set)' => '(не задано)',
'An internal server error occurred.' => 'Виникла внутрішня помилка сервера.', 'An internal server error occurred.' => 'Виникла внутрішня помилка сервера.',
'Are you sure to delete this item?' => 'Ви впевнені, що хочете видалити цей елемент?', 'Are you sure you want to delete this item?' => 'Ви впевнені, що хочете видалити цей елемент?',
'Delete' => 'Видалити', 'Delete' => 'Видалити',
'Error' => 'Помилка', 'Error' => 'Помилка',
'File upload failed.' => 'Завантаження файлу не вдалося.', 'File upload failed.' => 'Завантаження файлу не вдалося.',
......
...@@ -20,7 +20,7 @@ return array ( ...@@ -20,7 +20,7 @@ return array (
'the input value' => '该输入', 'the input value' => '该输入',
'(not set)' => '(未设置)', '(not set)' => '(未设置)',
'An internal server error occurred.' => '服务器内部错误。', 'An internal server error occurred.' => '服务器内部错误。',
'Are you sure to delete this item?' => '确定要删除这条数据吗?', 'Are you sure you want to delete this item?' => '您确定要删除此项吗?',
'Delete' => '删除', 'Delete' => '删除',
'Error' => '错误', 'Error' => '错误',
'File upload failed.' => '文件上传失败。', 'File upload failed.' => '文件上传失败。',
......
...@@ -310,13 +310,17 @@ class UrlManager extends Component ...@@ -310,13 +310,17 @@ class UrlManager extends Component
* It defaults to [[Request::scriptUrl]] if [[showScriptName]] is true or [[enablePrettyUrl]] is false; * It defaults to [[Request::scriptUrl]] if [[showScriptName]] is true or [[enablePrettyUrl]] is false;
* otherwise, it defaults to [[Request::baseUrl]]. * otherwise, it defaults to [[Request::baseUrl]].
* @return string the base URL that is used by [[createUrl()]] to prepend URLs it creates. * @return string the base URL that is used by [[createUrl()]] to prepend URLs it creates.
* @throws InvalidConfigException if running in console application and [[baseUrl]] is not configured.
*/ */
public function getBaseUrl() public function getBaseUrl()
{ {
if ($this->_baseUrl === null) { if ($this->_baseUrl === null) {
/** @var \yii\web\Request $request */
$request = Yii::$app->getRequest(); $request = Yii::$app->getRequest();
$this->_baseUrl = $this->showScriptName || !$this->enablePrettyUrl ? $request->getScriptUrl() : $request->getBaseUrl(); if ($request instanceof \yii\web\Request) {
$this->_baseUrl = $this->showScriptName || !$this->enablePrettyUrl ? $request->getScriptUrl() : $request->getBaseUrl();
} else {
throw new InvalidConfigException('Please configure UrlManager::baseUrl correctly as you are running a console application.');
}
} }
return $this->_baseUrl; return $this->_baseUrl;
...@@ -334,11 +338,17 @@ class UrlManager extends Component ...@@ -334,11 +338,17 @@ class UrlManager extends Component
/** /**
* Returns the host info that is used by [[createAbsoluteUrl()]] to prepend URLs it creates. * Returns the host info that is used by [[createAbsoluteUrl()]] to prepend URLs it creates.
* @return string the host info (e.g. "http://www.example.com") that is used by [[createAbsoluteUrl()]] to prepend URLs it creates. * @return string the host info (e.g. "http://www.example.com") that is used by [[createAbsoluteUrl()]] to prepend URLs it creates.
* @throws InvalidConfigException if running in console application and [[hostInfo]] is not configured.
*/ */
public function getHostInfo() public function getHostInfo()
{ {
if ($this->_hostInfo === null) { if ($this->_hostInfo === null) {
$this->_hostInfo = Yii::$app->getRequest()->getHostInfo(); $request = Yii::$app->getRequest();
if ($request instanceof \yii\web\Request) {
$this->_hostInfo = $request->getHostInfo();
} else {
throw new InvalidConfigException('Please configure UrlManager::hostInfo correctly as you are running a console application.');
}
} }
return $this->_hostInfo; return $this->_hostInfo;
......
...@@ -324,9 +324,7 @@ class User extends Component ...@@ -324,9 +324,7 @@ class User extends Component
$url = Yii::$app->getSession()->get($this->returnUrlParam, $defaultUrl); $url = Yii::$app->getSession()->get($this->returnUrlParam, $defaultUrl);
if (is_array($url)) { if (is_array($url)) {
if (isset($url[0])) { if (isset($url[0])) {
$route = array_shift($url); return Yii::$app->getUrlManager()->createUrl($url);
return Yii::$app->getUrlManager()->createUrl($route, $url);
} else { } else {
$url = null; $url = null;
} }
......
<?php
namespace yiiunit\data\ar;
/**
* Model representing tbl_type table
*
* @property int $int_col
* @property int $int_col2 DEFAULT 1
* @property string $char_col
* @property string $char_col2 DEFAULT 'something'
* @property string $char_col3
* @property float $float_col
* @property float $float_col2 DEFAULT '1.23'
* @property string $blob_col
* @property float $numeric_col DEFAULT '33.22'
* @property string $time DEFAULT '2002-01-01 00:00:00'
* @property boolean $bool_col
* @property boolean $bool_col2 DEFAULT 1
*/
class Type extends ActiveRecord
{
/**
* @inheritdoc
*/
public static function tableName()
{
return 'tbl_type';
}
}
\ No newline at end of file
...@@ -91,7 +91,9 @@ CREATE TABLE `tbl_type` ( ...@@ -91,7 +91,9 @@ CREATE TABLE `tbl_type` (
`float_col2` double DEFAULT '1.23', `float_col2` double DEFAULT '1.23',
`blob_col` blob, `blob_col` blob,
`numeric_col` decimal(5,2) DEFAULT '33.22', `numeric_col` decimal(5,2) DEFAULT '33.22',
`time` timestamp NOT NULL DEFAULT '2002-01-01 00:00:00' `time` timestamp NOT NULL DEFAULT '2002-01-01 00:00:00',
`bool_col` tinyint NOT NULL,
`bool_col2` tinyint DEFAULT '1'
); );
CREATE TABLE `tbl_composite_fk` ( CREATE TABLE `tbl_composite_fk` (
......
...@@ -8,6 +8,8 @@ namespace yiiunit\framework\base; ...@@ -8,6 +8,8 @@ namespace yiiunit\framework\base;
use yii\base\Formatter; use yii\base\Formatter;
use yiiunit\TestCase; use yiiunit\TestCase;
use DateTime;
use DateInterval;
/** /**
* @group base * @group base
...@@ -197,4 +199,156 @@ class FormatterTest extends TestCase ...@@ -197,4 +199,156 @@ class FormatterTest extends TestCase
$this->setExpectedException('\yii\base\InvalidParamException'); $this->setExpectedException('\yii\base\InvalidParamException');
$this->assertSame(date('Y-m-d', $value), $this->formatter->format($value, 'data')); $this->assertSame(date('Y-m-d', $value), $this->formatter->format($value, 'data'));
} }
private function buildDateSubIntervals($referenceDate, $intervals)
{
$date = new DateTime($referenceDate);
foreach ($intervals as $interval) {
$date->sub($interval);
}
return $date;
}
public function testAsRelativeTime()
{
$interval_1_second = new DateInterval("PT1S");
$interval_244_seconds = new DateInterval("PT244S");
$interval_1_minute = new DateInterval("PT1M");
$interval_33_minutes = new DateInterval("PT33M");
$interval_1_hour = new DateInterval("PT1H");
$interval_6_hours = new DateInterval("PT6H");
$interval_1_day = new DateInterval("P1D");
$interval_89_days = new DateInterval("P89D");
$interval_1_month = new DateInterval("P1M");
$interval_5_months = new DateInterval("P5M");
$interval_1_year = new DateInterval("P1Y");
$interval_12_years = new DateInterval("P12Y");
// Pass a DateInterval
$this->assertSame('a second ago', $this->formatter->asRelativeTime($interval_1_second));
$this->assertSame('244 seconds ago', $this->formatter->asRelativeTime($interval_244_seconds));
$this->assertSame('a minute ago', $this->formatter->asRelativeTime($interval_1_minute));
$this->assertSame('33 minutes ago', $this->formatter->asRelativeTime($interval_33_minutes));
$this->assertSame('an hour ago', $this->formatter->asRelativeTime($interval_1_hour));
$this->assertSame('6 hours ago', $this->formatter->asRelativeTime($interval_6_hours));
$this->assertSame('a day ago', $this->formatter->asRelativeTime($interval_1_day));
$this->assertSame('89 days ago', $this->formatter->asRelativeTime($interval_89_days));
$this->assertSame('a month ago', $this->formatter->asRelativeTime($interval_1_month));
$this->assertSame('5 months ago', $this->formatter->asRelativeTime($interval_5_months));
$this->assertSame('a year ago', $this->formatter->asRelativeTime($interval_1_year));
$this->assertSame('12 years ago', $this->formatter->asRelativeTime($interval_12_years));
// Pass a DateInterval string
$this->assertSame('a year ago', $this->formatter->asRelativeTime('2007-03-01T13:00:00Z/2008-05-11T15:30:00Z'));
$this->assertSame('a year ago', $this->formatter->asRelativeTime('2007-03-01T13:00:00Z/P1Y2M10DT2H30M'));
$this->assertSame('a year ago', $this->formatter->asRelativeTime('P1Y2M10DT2H30M/2008-05-11T15:30:00Z'));
$this->assertSame('a year ago', $this->formatter->asRelativeTime('P1Y2M10DT2H30M'));
$this->assertSame('94 months ago', $this->formatter->asRelativeTime('P94M'));
// Force the reference time and pass a past DateTime
$dateNow = new DateTime('2014-03-13');
$this->assertSame('a second ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_1_second]), $dateNow));
$this->assertSame('4 minutes ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_244_seconds]), $dateNow));
$this->assertSame('a minute ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_1_minute]), $dateNow));
$this->assertSame('33 minutes ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_33_minutes]), $dateNow));
$this->assertSame('an hour ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_1_hour]), $dateNow));
$this->assertSame('6 hours ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_6_hours]), $dateNow));
$this->assertSame('a day ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_1_day]), $dateNow));
$this->assertSame('2 months ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_89_days]), $dateNow));
$this->assertSame('a month ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_1_month]), $dateNow));
$this->assertSame('5 months ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_5_months]), $dateNow));
$this->assertSame('a year ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_1_year]), $dateNow));
$this->assertSame('12 years ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_12_years]), $dateNow));
// Tricky 31-days month stuff
// See: http://www.gnu.org/software/tar/manual/html_section/Relative-items-in-date-strings.html
$dateNow = new DateTime('2014-03-31');
$dateThen = new DateTime('2014-03-03');
$this->assertSame('28 days ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-31', [$interval_1_month]), $dateNow));
$this->assertSame('28 days ago', $this->formatter->asRelativeTime($dateThen, $dateNow));
$dateThen = new DateTime('2014-02-28');
$this->assertSame('a month ago', $this->formatter->asRelativeTime($dateThen, $dateNow));
// Relative to current time tests (can't test with seconds though due to the tests computation time)
$this->assertSame('4 minutes ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_244_seconds])));
$this->assertSame('a minute ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_1_minute])));
$this->assertSame('33 minutes ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_33_minutes])));
$this->assertSame('an hour ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_1_hour])));
$this->assertSame('6 hours ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_6_hours])));
$this->assertSame('a day ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_1_day])));
$this->assertSame('2 months ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_89_days])));
$this->assertSame('a month ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_1_month])));
$this->assertSame('5 months ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_5_months])));
$this->assertSame('a year ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_1_year])));
$this->assertSame('12 years ago', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_12_years])));
// Invert all the DateIntervals
$interval_1_second->invert = true;
$interval_244_seconds->invert = true;
$interval_1_minute->invert = true;
$interval_33_minutes->invert = true;
$interval_1_hour->invert = true;
$interval_6_hours->invert = true;
$interval_1_day->invert = true;
$interval_89_days->invert = true;
$interval_1_month->invert = true;
$interval_5_months->invert = true;
$interval_1_year->invert = true;
$interval_12_years->invert = true;
// Pass a inverted DateInterval
$this->assertSame('in a second', $this->formatter->asRelativeTime($interval_1_second));
$this->assertSame('in 244 seconds', $this->formatter->asRelativeTime($interval_244_seconds));
$this->assertSame('in a minute', $this->formatter->asRelativeTime($interval_1_minute));
$this->assertSame('in 33 minutes', $this->formatter->asRelativeTime($interval_33_minutes));
$this->assertSame('in an hour', $this->formatter->asRelativeTime($interval_1_hour));
$this->assertSame('in 6 hours', $this->formatter->asRelativeTime($interval_6_hours));
$this->assertSame('in a day', $this->formatter->asRelativeTime($interval_1_day));
$this->assertSame('in 89 days', $this->formatter->asRelativeTime($interval_89_days));
$this->assertSame('in a month', $this->formatter->asRelativeTime($interval_1_month));
$this->assertSame('in 5 months', $this->formatter->asRelativeTime($interval_5_months));
$this->assertSame('in a year', $this->formatter->asRelativeTime($interval_1_year));
$this->assertSame('in 12 years', $this->formatter->asRelativeTime($interval_12_years));
// Pass a inverted DateInterval string
$this->assertSame('in a year', $this->formatter->asRelativeTime('2008-05-11T15:30:00Z/2007-03-01T13:00:00Z'));
// Force the reference time and pass a future DateTime
$dateNow = new DateTime('2014-03-13');
$this->assertSame('in a second', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_1_second]), $dateNow));
$this->assertSame('in 4 minutes', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_244_seconds]), $dateNow));
$this->assertSame('in a minute', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_1_minute]), $dateNow));
$this->assertSame('in 33 minutes', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_33_minutes]), $dateNow));
$this->assertSame('in an hour', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_1_hour]), $dateNow));
$this->assertSame('in 6 hours', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_6_hours]), $dateNow));
$this->assertSame('in a day', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_1_day]), $dateNow));
$this->assertSame('in 2 months', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_89_days]), $dateNow));
$this->assertSame('in a month', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_1_month]), $dateNow));
$this->assertSame('in 5 months', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_5_months]), $dateNow));
$this->assertSame('in a year', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_1_year]), $dateNow));
$this->assertSame('in 12 years', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-13', [$interval_12_years]), $dateNow));
// Tricky 31-days month stuff
// See: http://www.gnu.org/software/tar/manual/html_section/Relative-items-in-date-strings.html
$dateNow = new DateTime('2014-03-03');
$dateThen = new DateTime('2014-03-31');
$this->assertSame('in a month', $this->formatter->asRelativeTime($this->buildDateSubIntervals('2014-03-03', [$interval_1_month]), $dateNow));
$this->assertSame('in 28 days', $this->formatter->asRelativeTime($dateThen, $dateNow));
// Relative to current time tests (can't test with seconds though due to the tests computation time)
// We add 5 seconds to compensate for tests computation time
$interval_5_seconds = new DateInterval('PT5S');
$interval_5_seconds->invert = true;
$this->assertSame('in 4 minutes', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_244_seconds, $interval_5_seconds])));
$this->assertSame('in a minute', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_1_minute, $interval_5_seconds])));
$this->assertSame('in 33 minutes', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_33_minutes, $interval_5_seconds])));
$this->assertSame('in an hour', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_1_hour, $interval_5_seconds])));
$this->assertSame('in 6 hours', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_6_hours, $interval_5_seconds])));
$this->assertSame('in a day', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_1_day, $interval_5_seconds])));
$this->assertSame('in 2 months', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_89_days, $interval_5_seconds])));
$this->assertSame('in a month', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_1_month, $interval_5_seconds])));
$this->assertSame('in 5 months', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_5_months, $interval_5_seconds])));
$this->assertSame('in a year', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_1_year, $interval_5_seconds])));
$this->assertSame('in 12 years', $this->formatter->asRelativeTime($this->buildDateSubIntervals('now', [$interval_12_years, $interval_5_seconds])));
}
} }
...@@ -8,7 +8,9 @@ use yiiunit\data\ar\OrderItem; ...@@ -8,7 +8,9 @@ use yiiunit\data\ar\OrderItem;
use yiiunit\data\ar\Order; use yiiunit\data\ar\Order;
use yiiunit\data\ar\Item; use yiiunit\data\ar\Item;
use yiiunit\data\ar\Profile; use yiiunit\data\ar\Profile;
use yiiunit\data\ar\Type;
use yiiunit\framework\ar\ActiveRecordTestTrait; use yiiunit\framework\ar\ActiveRecordTestTrait;
use yiiunit\framework\db\cubrid\CubridActiveRecordTest;
/** /**
* @group db * @group db
...@@ -486,4 +488,34 @@ class ActiveRecordTest extends DatabaseTestCase ...@@ -486,4 +488,34 @@ class ActiveRecordTest extends DatabaseTestCase
$this->assertTrue($orders[1]['customer2']['orders2'][0]['id'] === $orders[0]['id']); $this->assertTrue($orders[1]['customer2']['orders2'][0]['id'] === $orders[0]['id']);
$this->assertTrue($orders[1]['customer2']['orders2'][1]['id'] === $orders[1]['id']); $this->assertTrue($orders[1]['customer2']['orders2'][1]['id'] === $orders[1]['id']);
} }
public function testDefaultValues()
{
$model = new Type();
$model->loadDefaultValues();
$this->assertEquals(1, $model->int_col2);
$this->assertEquals('something', $model->char_col2);
$this->assertEquals(1.23, $model->float_col2);
$this->assertEquals(33.22, $model->numeric_col);
$this->assertEquals(true, $model->bool_col2);
if ($this instanceof CubridActiveRecordTest) {
// cubrid has non-standard timestamp representation
$this->assertEquals('12:00:00 AM 01/01/2002', $model->time);
} else {
$this->assertEquals('2002-01-01 00:00:00', $model->time);
}
$model = new Type();
$model->char_col2 = 'not something';
$model->loadDefaultValues();
$this->assertEquals('not something', $model->char_col2);
$model = new Type();
$model->char_col2 = 'not something';
$model->loadDefaultValues(false);
$this->assertEquals('something', $model->char_col2);
}
} }
...@@ -31,7 +31,7 @@ class CubridCommandTest extends CommandTest ...@@ -31,7 +31,7 @@ class CubridCommandTest extends CommandTest
$command->bindParam(':email', $email); $command->bindParam(':email', $email);
$this->assertEquals($name, $command->queryScalar()); $this->assertEquals($name, $command->queryScalar());
$sql = "INSERT INTO tbl_type (int_col, char_col, char_col2, enum_col, float_col, blob_col, numeric_col) VALUES (:int_col, '', :char_col, :enum_col, :float_col, CHAR_TO_BLOB(:blob_col), :numeric_col)"; $sql = "INSERT INTO tbl_type (int_col, char_col, char_col2, enum_col, float_col, blob_col, numeric_col, bool_col) VALUES (:int_col, '', :char_col, :enum_col, :float_col, CHAR_TO_BLOB(:blob_col), :numeric_col, :bool_col)";
$command = $db->createCommand($sql); $command = $db->createCommand($sql);
$intCol = 123; $intCol = 123;
$charCol = 'abc'; $charCol = 'abc';
...@@ -39,12 +39,14 @@ class CubridCommandTest extends CommandTest ...@@ -39,12 +39,14 @@ class CubridCommandTest extends CommandTest
$floatCol = 1.23; $floatCol = 1.23;
$blobCol = "\x10\x11\x12"; $blobCol = "\x10\x11\x12";
$numericCol = '1.23'; $numericCol = '1.23';
$boolCol = true;
$command->bindParam(':int_col', $intCol); $command->bindParam(':int_col', $intCol);
$command->bindParam(':char_col', $charCol); $command->bindParam(':char_col', $charCol);
$command->bindParam(':enum_col', $enumCol); $command->bindParam(':enum_col', $enumCol);
$command->bindParam(':float_col', $floatCol); $command->bindParam(':float_col', $floatCol);
$command->bindParam(':blob_col', $blobCol); $command->bindParam(':blob_col', $blobCol);
$command->bindParam(':numeric_col', $numericCol); $command->bindParam(':numeric_col', $numericCol);
$command->bindParam(':bool_col', $boolCol);
$this->assertEquals(1, $command->execute()); $this->assertEquals(1, $command->execute());
$sql = 'SELECT * FROM tbl_type'; $sql = 'SELECT * FROM tbl_type';
...@@ -55,6 +57,7 @@ class CubridCommandTest extends CommandTest ...@@ -55,6 +57,7 @@ class CubridCommandTest extends CommandTest
$this->assertEquals($floatCol, $row['float_col']); $this->assertEquals($floatCol, $row['float_col']);
$this->assertEquals($blobCol, fread($row['blob_col'], 3)); $this->assertEquals($blobCol, fread($row['blob_col'], 3));
$this->assertEquals($numericCol, $row['numeric_col']); $this->assertEquals($numericCol, $row['numeric_col']);
$this->assertEquals($boolCol, $row['bool_col']);
// bindValue // bindValue
$sql = 'INSERT INTO tbl_customer(email, name, address) VALUES (:email, \'user5\', \'address5\')'; $sql = 'INSERT INTO tbl_customer(email, name, address) VALUES (:email, \'user5\', \'address5\')';
......
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