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:
- [[yii\db\ActiveRecord::find()]]
- [[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
// to retrieve all *active* customers and order them by their ID:
$customers = Customer::find()
->where(['status' => $active])
->where(['status' => Customer::STATUS_ACTIVE])
->orderBy('id')
->all();
......@@ -99,7 +100,7 @@ $customers = Customer::findBySql($sql)->all();
// to return the number of *active* customers:
$count = Customer::find()
->where(['status' => $active])
->where(['status' => Customer::STATUS_ACTIVE])
->count();
// to return customers in terms of arrays rather than `Customer` objects:
......@@ -113,6 +114,10 @@ $customers = Customer::find()->indexBy('id')->all();
// $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,
```php
......@@ -193,6 +198,15 @@ Customer::updateAllCounters(['age' => 1]);
> 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`.
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
-------------------------
......
......@@ -53,7 +53,7 @@ class SiteController extends Controller
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
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.
~~~
```
server {
set $yii_bootstrap "index.php";
charset utf-8;
......@@ -159,6 +159,10 @@ server {
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.
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
- 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 #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)
- Chg #1459: Update Collapse to use bootstrap 3 classes (tonydspaniard)
- Chg #1820: Update Progress to use bootstrap 3 markup (samdark)
2.0.0 alpha, December 1, 2013
-----------------------------
......
......@@ -75,6 +75,10 @@ class Nav extends Widget
*/
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.
* If not set, it will use the route of the current request.
* @see params
......@@ -156,16 +160,15 @@ class Nav extends Widget
$active = $this->isItemActive($item);
}
if ($active) {
Html::addCssClass($options, 'active');
}
if ($items !== null) {
$linkOptions['data-toggle'] = 'dropdown';
Html::addCssClass($options, 'dropdown');
Html::addCssClass($linkOptions, 'dropdown-toggle');
$label .= ' ' . Html::tag('b', '', ['class' => 'caret']);
if (is_array($items)) {
if ($this->activateItems) {
$items = $this->isChildActive($items, $active);
}
$items = Dropdown::widget([
'items' => $items,
'encodeLabels' => $this->encodeLabels,
......@@ -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);
}
/**
* 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.
......
......@@ -47,6 +47,15 @@ abstract class Generator extends Model
* The value of this property is internally managed by this class.
*/
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
......@@ -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.
* Derived classes usually should override this method if they require the existence of
* certain template files.
......@@ -95,7 +115,7 @@ abstract class Generator extends Model
*/
public function stickyAttributes()
{
return ['template'];
return ['template', 'enableI18N', 'messageCategory'];
}
/**
......@@ -106,7 +126,11 @@ abstract class Generator extends Model
*/
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
}
/**
* 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
* @return boolean whether the value is a reserved PHP keyword.
*/
......@@ -448,4 +482,38 @@ abstract class Generator extends Model
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 ($) {
initConfirmationCheckboxes();
// 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);
}).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
$('.default-view .form-group input,select,textarea').change(function () {
$('.default-view-results,.default-view-files').hide();
......
......@@ -71,6 +71,8 @@ class Generator extends \yii\gii\Generator
[['indexWidgetType'], 'in', 'range' => ['grid', 'list']],
[['modelClass'], 'validateModelClass'],
[['moduleID'], 'validateModuleID'],
[['enableI18N'], 'boolean'],
[['messageCategory'], 'validateMessageCategory', 'skipOnEmpty' => false],
]);
}
......@@ -94,7 +96,7 @@ class Generator extends \yii\gii\Generator
*/
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.
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
......@@ -107,7 +109,7 @@ class Generator extends \yii\gii\Generator
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
qualified namespaced class name, e.g., <code>app\models\search\PostSearch</code>.',
];
]);
}
/**
......@@ -123,7 +125,7 @@ class Generator extends \yii\gii\Generator
*/
public function stickyAttributes()
{
return ['baseControllerClass', 'moduleID', 'indexWidgetType'];
return array_merge(parent::stickyAttributes(), ['baseControllerClass', 'moduleID', 'indexWidgetType']);
}
/**
......
......@@ -3,7 +3,7 @@
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\gii\generators\crud\Generator $generator
......@@ -49,7 +49,7 @@ class <?= $searchModelClass ?> extends Model
{
return [
<?php foreach ($labels as $name => $label): ?>
<?= "'$name' => '" . addslashes($label) . "',\n" ?>
<?= "'$name' => " . $generator->generateString($label) . ",\n" ?>
<?php endforeach; ?>
];
}
......
......@@ -36,7 +36,7 @@ use yii\widgets\ActiveForm;
echo " <?= " . $generator->generateActiveField($attribute) . " ?>\n\n";
} ?>
<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>
<?= "<?php " ?>ActiveForm::end(); ?>
......
......@@ -39,8 +39,8 @@ foreach ($generator->getColumnNames() as $attribute) {
}
?>
<div class="form-group">
<?= "<?= " ?>Html::submitButton('Search', ['class' => 'btn btn-primary']) ?>
<?= "<?= " ?>Html::resetButton('Reset', ['class' => 'btn btn-default']) ?>
<?= "<?= " ?>Html::submitButton(<?= $generator->generateString('Search') ?>, ['class' => 'btn btn-primary']) ?>
<?= "<?= " ?>Html::resetButton(<?= $generator->generateString('Reset') ?>, ['class' => 'btn btn-default']) ?>
</div>
<?= "<?php " ?>ActiveForm::end(); ?>
......
......@@ -18,8 +18,8 @@ use yii\helpers\Html;
* @var <?= ltrim($generator->modelClass, '\\') ?> $model
*/
$this->title = 'Create <?= Inflector::camel2words(StringHelper::basename($generator->modelClass)) ?>';
$this->params['breadcrumbs'][] = ['label' => '<?= Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass))) ?>', 'url' => ['index']];
$this->title = <?= $generator->generateString('Create {modelClass}', ['modelClass' => Inflector::camel2words(StringHelper::basename($generator->modelClass))]) ?>;
$this->params['breadcrumbs'][] = ['label' => <?= $generator->generateString(Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass)))) ?>, 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="<?= Inflector::camel2id(StringHelper::basename($generator->modelClass)) ?>-create">
......
......@@ -23,7 +23,7 @@ use <?= $generator->indexWidgetType === 'grid' ? "yii\\grid\\GridView" : "yii\\w
* @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;
?>
<div class="<?= Inflector::camel2id(StringHelper::basename($generator->modelClass)) ?>-index">
......@@ -33,7 +33,7 @@ $this->params['breadcrumbs'][] = $this->title;
<?= "<?php " . ($generator->indexWidgetType === 'grid' ? "// " : "") ?>echo $this->render('_search', ['model' => $searchModel]); ?>
<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>
<?php if ($generator->indexWidgetType === 'grid'): ?>
......
......@@ -20,10 +20,10 @@ use yii\helpers\Html;
* @var <?= ltrim($generator->modelClass, '\\') ?> $model
*/
$this->title = 'Update <?= Inflector::camel2words(StringHelper::basename($generator->modelClass)) ?>: ' . $model-><?= $generator->getNameAttribute() ?>;
$this->params['breadcrumbs'][] = ['label' => '<?= Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass))) ?>', 'url' => ['index']];
$this->title = <?= $generator->generateString('Update {modelClass}: ', ['modelClass' => Inflector::camel2words(StringHelper::basename($generator->modelClass))]) ?> . $model-><?= $generator->getNameAttribute() ?>;
$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'][] = 'Update';
$this->params['breadcrumbs'][] = <?= $generator->generateString('Update') ?>;
?>
<div class="<?= Inflector::camel2id(StringHelper::basename($generator->modelClass)) ?>-update">
......
......@@ -22,7 +22,7 @@ use yii\widgets\DetailView;
*/
$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;
?>
<div class="<?= Inflector::camel2id(StringHelper::basename($generator->modelClass)) ?>-view">
......@@ -30,11 +30,11 @@ $this->params['breadcrumbs'][] = $this->title;
<h1><?= "<?= " ?>Html::encode($this->title) ?></h1>
<p>
<?= "<?= " ?>Html::a('Update', ['update', <?= $urlParams ?>], ['class' => 'btn btn-primary']) ?>
<?= "<?= " ?>Html::a('Delete', ['delete', <?= $urlParams ?>], [
<?= "<?= " ?>Html::a(<?= $generator->generateString('Update') ?>, ['update', <?= $urlParams ?>], ['class' => 'btn btn-primary']) ?>
<?= "<?= " ?>Html::a(<?= $generator->generateString('Delete') ?>, ['delete', <?= $urlParams ?>], [
'class' => 'btn btn-danger',
'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',
],
]) ?>
......
......@@ -14,3 +14,5 @@ echo $form->field($generator, 'indexWidgetType')->dropDownList([
'grid' => 'GridView',
'list' => 'ListView',
]);
echo $form->field($generator, 'enableI18N')->checkbox();
echo $form->field($generator, 'messageCategory');
......@@ -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 #2740: Fixed the issue that `CaptchaAction::run()` was using obsolete `Controller::createUrl()` method (tonydspaniard)
- 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 incorrect event name for `yii\jui\Spinner` (samdark)
- Bug: Json::encode() did not handle objects that implement JsonSerializable interface correctly (cebe)
......@@ -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 #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 #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 #2735: Added support for `DateTimeInterface` in `Formatter` (ivokund)
- Enh #2756: Added support for injecting custom `isEmpty` check for all validators (qiangxue)
......
......@@ -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);
}
}
/**
* 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,13 +82,12 @@ class Theme extends Component
{
parent::init();
if (($basePath = $this->getBasePath()) !== null) {
if (empty($this->pathMap)) {
$this->pathMap = [Yii::$app->getBasePath() => [$basePath]];
}
} else {
if (($basePath = $this->getBasePath()) === null) {
throw new InvalidConfigException('The "basePath" property must be set.');
}
$this->pathMap = [Yii::$app->getBasePath() => [$basePath]];
}
}
private $_baseUrl;
......@@ -173,6 +172,7 @@ class Theme extends Component
}
}
/**
* Converts a relative file path into an absolute one using [[basePath]].
* @param string $path the relative file path to be converted.
......@@ -180,6 +180,10 @@ class Theme extends Component
*/
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
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.
* 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.
......
......@@ -17,7 +17,8 @@ use yii\base\InvalidCallException;
* 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()) {
* $rows[] = $row;
......
......@@ -20,9 +20,13 @@ class QueryBuilder extends \yii\db\QueryBuilder
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 = [
$this->buildSelect($query->select, $params, $query->distinct, $query->selectOption),
$this->buildFrom($query->from, $params),
......
......@@ -373,6 +373,12 @@ SQL;
$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;
......@@ -398,7 +404,6 @@ SQL;
$column->precision = $info['numeric_precision'];
$column->scale = $info['numeric_scale'];
$column->size = $info['size'];
if (isset($this->typeMap[$column->dbType])) {
$column->type = $this->typeMap[$column->dbType];
} else {
......@@ -406,6 +411,7 @@ SQL;
}
$column->phpType = $this->getColumnPhpType($column);
return $column;
}
}
......@@ -238,9 +238,9 @@ class Schema extends \yii\db\Schema
}
$column->phpType = $this->getColumnPhpType($column);
$value = $info['dflt_value'];
$value = trim($info['dflt_value'], "'\"");
if ($column->type === 'string') {
$column->defaultValue = trim($value, "'\"");
$column->defaultValue = $value;
} else {
$column->defaultValue = $column->typecast(strcasecmp($value, 'null') ? $value : null);
}
......
......@@ -54,14 +54,14 @@ class I18N extends Component
public function init()
{
parent::init();
if (!isset($this->translations['yii'])) {
if (!isset($this->translations['yii']) && !isset($this->translations['yii*'])) {
$this->translations['yii'] = [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => 'en',
'basePath' => '@yii/messages',
];
}
if (!isset($this->translations['app'])) {
if (!isset($this->translations['app']) && !isset($this->translations['app*'])) {
$this->translations['app'] = [
'class' => 'yii\i18n\PhpMessageSource',
'sourceLanguage' => Yii::$app->sourceLanguage,
......
......@@ -19,7 +19,7 @@
return array (
'(not set)' => '(ikke defineret)',
'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',
'Error' => 'Fejl',
'File upload failed.' => 'Upload af fil fejlede.',
......@@ -32,8 +32,8 @@ return array (
'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 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 mimeTypes are allowed: {mimeTypes}.' => 'Kun filer med følgende mime-typer er tilladte: {mimeTypes}.',
'Page not found.' => 'Siden blev ikke fundet.',
'Please fix the following errors:' => 'Ret venligst følgende fejl:',
'Please upload a file.' => 'Venligst upload en fil.',
......@@ -56,6 +56,12 @@ return array (
'Yes' => 'Ja',
'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}}.',
'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',
'{attribute} "{value}" has already been taken.' => '{attribute} "{value}" er allerede i brug.',
'{attribute} cannot be blank.' => '{attribute} må ikke være tom.',
......@@ -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 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}}.',
'{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 @@
* NOTE: this file must be saved in UTF-8 encoding.
*/
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)',
'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',
'Error' => 'Fehler',
'File upload failed.' => 'Das Hochladen der Datei ist gescheitert.',
......@@ -34,7 +45,7 @@ return array (
'No help for unknown sub-command "{command}".' => 'Es gibt keine Hilfe für den unbekannten Unterbefehl "{command}".',
'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 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.',
'Please fix the following errors:' => 'Bitte korrigieren Sie die folgenden Fehler:',
'Please upload a file.' => 'Bitte laden Sie eine Datei hoch.',
......@@ -57,6 +68,7 @@ return array (
'Yes' => 'Ja',
'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.',
'the input value' => 'der eingegebene Wert',
'{attribute} "{value}" has already been taken.' => '{attribute} "{value}" wird bereits verwendet.',
'{attribute} cannot be blank.' => '{attribute} darf nicht leer sein.',
'{attribute} is invalid.' => '{attribute} ist ungültig.',
......
......@@ -17,9 +17,36 @@
* NOTE: this file must be saved in UTF-8 encoding.
*/
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)',
'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',
'Error' => 'Error',
'File upload failed.' => 'Falló la subida del archivo.',
......@@ -33,7 +60,6 @@ return array (
'No help for unknown sub-command "{command}".' => 'No existe ayuda para el sub-comando desconocido "{command}"',
'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 mimeTypes are allowed: {mimeTypes}.' => 'Sólo se aceptan archivos con los siguientes tipos mime: {mimeTypes}',
'Page not found.' => 'Página no entontrada.',
'Please fix the following errors:' => 'Por favor corrija los siguientes errores:',
'Please upload a file.' => 'Por favor suba un archivo.',
......
......@@ -17,9 +17,36 @@
* NOTE: this file must be saved in UTF-8 encoding.
*/
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)',
'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',
'Error' => 'Erreur',
'File upload failed.' => 'Le téléchargement du fichier a échoué.',
......@@ -33,7 +60,6 @@ return array (
'No help for unknown sub-command "{command}".' => 'Aucune aide pour la sous-commande inconnue « {command} ».',
'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 mimeTypes are allowed: {mimeTypes}.' => 'Les types MIME de fichier autorisés sont : {mimeTypes}.',
'Page not found.' => 'Page non trouvée.',
'Please fix the following errors:' => 'Veuillez vérifier les erreurs suivantes :',
'Please upload a file.' => 'Veuillez télécharger un fichier.',
......
......@@ -20,7 +20,7 @@ return array (
'View' => 'Просмотр',
'(not set)' => '(не задано)',
'An internal server error occurred.' => 'Возникла внутренняя ошибка сервера.',
'Are you sure to delete this item?' => 'Вы уверены, что хотите удалить этот элемент?',
'Are you sure you want to delete this item?' => 'Вы уверены, что хотите удалить этот элемент?',
'Delete' => 'Удалить',
'Error' => 'Ошибка',
'File upload failed.' => 'Загрузка файла не удалась.',
......
......@@ -20,7 +20,7 @@ return array (
'View' => 'Перегляд',
'(not set)' => '(не задано)',
'An internal server error occurred.' => 'Виникла внутрішня помилка сервера.',
'Are you sure to delete this item?' => 'Ви впевнені, що хочете видалити цей елемент?',
'Are you sure you want to delete this item?' => 'Ви впевнені, що хочете видалити цей елемент?',
'Delete' => 'Видалити',
'Error' => 'Помилка',
'File upload failed.' => 'Завантаження файлу не вдалося.',
......
......@@ -20,7 +20,7 @@ return array (
'the input value' => '该输入',
'(not set)' => '(未设置)',
'An internal server error occurred.' => '服务器内部错误。',
'Are you sure to delete this item?' => '确定要删除这条数据吗?',
'Are you sure you want to delete this item?' => '您确定要删除此项吗?',
'Delete' => '删除',
'Error' => '错误',
'File upload failed.' => '文件上传失败。',
......
......@@ -310,13 +310,17 @@ class UrlManager extends Component
* It defaults to [[Request::scriptUrl]] if [[showScriptName]] is true or [[enablePrettyUrl]] is false;
* otherwise, it defaults to [[Request::baseUrl]].
* @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()
{
if ($this->_baseUrl === null) {
/** @var \yii\web\Request $request */
$request = Yii::$app->getRequest();
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;
......@@ -334,11 +338,17 @@ class UrlManager extends Component
/**
* 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.
* @throws InvalidConfigException if running in console application and [[hostInfo]] is not configured.
*/
public function getHostInfo()
{
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;
......
......@@ -324,9 +324,7 @@ class User extends Component
$url = Yii::$app->getSession()->get($this->returnUrlParam, $defaultUrl);
if (is_array($url)) {
if (isset($url[0])) {
$route = array_shift($url);
return Yii::$app->getUrlManager()->createUrl($route, $url);
return Yii::$app->getUrlManager()->createUrl($url);
} else {
$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` (
`float_col2` double DEFAULT '1.23',
`blob_col` blob,
`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` (
......
......@@ -8,6 +8,8 @@ namespace yiiunit\framework\base;
use yii\base\Formatter;
use yiiunit\TestCase;
use DateTime;
use DateInterval;
/**
* @group base
......@@ -197,4 +199,156 @@ class FormatterTest extends TestCase
$this->setExpectedException('\yii\base\InvalidParamException');
$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;
use yiiunit\data\ar\Order;
use yiiunit\data\ar\Item;
use yiiunit\data\ar\Profile;
use yiiunit\data\ar\Type;
use yiiunit\framework\ar\ActiveRecordTestTrait;
use yiiunit\framework\db\cubrid\CubridActiveRecordTest;
/**
* @group db
......@@ -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'][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
$command->bindParam(':email', $email);
$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);
$intCol = 123;
$charCol = 'abc';
......@@ -39,12 +39,14 @@ class CubridCommandTest extends CommandTest
$floatCol = 1.23;
$blobCol = "\x10\x11\x12";
$numericCol = '1.23';
$boolCol = true;
$command->bindParam(':int_col', $intCol);
$command->bindParam(':char_col', $charCol);
$command->bindParam(':enum_col', $enumCol);
$command->bindParam(':float_col', $floatCol);
$command->bindParam(':blob_col', $blobCol);
$command->bindParam(':numeric_col', $numericCol);
$command->bindParam(':bool_col', $boolCol);
$this->assertEquals(1, $command->execute());
$sql = 'SELECT * FROM tbl_type';
......@@ -55,6 +57,7 @@ class CubridCommandTest extends CommandTest
$this->assertEquals($floatCol, $row['float_col']);
$this->assertEquals($blobCol, fread($row['blob_col'], 3));
$this->assertEquals($numericCol, $row['numeric_col']);
$this->assertEquals($boolCol, $row['bool_col']);
// bindValue
$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