Commit 76705b43 by Alexander Makarov

Merge branch 'master'

Conflicts: tests/unit/data/ar/elasticsearch/Customer.php
parents 504fc76f 4b7c879c
......@@ -17,8 +17,8 @@ use yii\web\IdentityInterface;
* @property string $auth_key
* @property integer $role
* @property integer $status
* @property integer $create_time
* @property integer $update_time
* @property integer $created_at
* @property integer $updated_at
*/
class User extends ActiveRecord implements IdentityInterface
{
......@@ -38,8 +38,8 @@ class User extends ActiveRecord implements IdentityInterface
'timestamp' => [
'class' => 'yii\behaviors\AutoTimestamp',
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => ['create_time', 'update_time'],
ActiveRecord::EVENT_BEFORE_UPDATE => 'update_time',
ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
ActiveRecord::EVENT_BEFORE_UPDATE => 'updated_at',
],
],
];
......
......@@ -21,8 +21,8 @@ class m130524_201442_init extends \yii\db\Migration
'role' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 10',
'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 10',
'create_time' => Schema::TYPE_INTEGER.' NOT NULL',
'update_time' => Schema::TYPE_INTEGER.' NOT NULL',
'created_at' => Schema::TYPE_INTEGER . ' NOT NULL',
'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL',
], $tableOptions);
}
......
......@@ -11,8 +11,9 @@
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
// fcgi doesn't have STDIN defined by default
// fcgi doesn't have STDIN and STDOUT defined by default
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
defined('STDIN') or define('STDOUT', fopen('php://stdout', 'w'));
require(__DIR__ . '/vendor/autoload.php');
require(__DIR__ . '/vendor/yiisoft/yii2/Yii.php');
......
......@@ -11,8 +11,9 @@
defined('YII_DEBUG') or define('YII_DEBUG', false);
defined('YII_ENV') or define('YII_ENV', 'prod');
// fcgi doesn't have STDIN defined by default
// fcgi doesn't have STDIN and STDOUT defined by default
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
defined('STDIN') or define('STDOUT', fopen('php://stdout', 'w'));
require(__DIR__ . '/vendor/autoload.php');
require(__DIR__ . '/vendor/yiisoft/yii2/Yii.php');
......
......@@ -16,7 +16,7 @@ $frameworkPath = dirname(__FILE__) . '/vendor/yiisoft/yii2';
if (!is_dir($frameworkPath)) {
echo '<h1>Error</h1>';
echo '<p><strong>The path to yii framework seems to be incorrect.</strong></p>';
echo '<p>You need to install Yii framework via composer or adjust the framework path in file <abbr title="' . __FILE__ . '">' . basename(__FILE__) .'</abbr>.</p>';
echo '<p>You need to install Yii framework via composer or adjust the framework path in file <abbr title="' . __FILE__ . '">' . basename(__FILE__) . '</abbr>.</p>';
echo '<p>Please refer to the <abbr title="' . dirname(__FILE__) . '/README.md">README</abbr> on how to install Yii.</p>';
}
......
......@@ -25,10 +25,6 @@ return [
],
],
'db' => $db,
'fixture' => [
'class' => 'yii\test\DbFixtureManager',
'basePath' => '@tests/unit/fixtures',
],
],
'params' => $params,
];
......@@ -16,7 +16,7 @@ $frameworkPath = dirname(__FILE__) . '/vendor/yiisoft/yii2';
if (!is_dir($frameworkPath)) {
echo '<h1>Error</h1>';
echo '<p><strong>The path to yii framework seems to be incorrect.</strong></p>';
echo '<p>You need to install Yii framework via composer or adjust the framework path in file <abbr title="' . __FILE__ . '">' . basename(__FILE__) .'</abbr>.</p>';
echo '<p>You need to install Yii framework via composer or adjust the framework path in file <abbr title="' . __FILE__ . '">' . basename(__FILE__) . '</abbr>.</p>';
echo '<p>Please refer to the <abbr title="' . dirname(__FILE__) . '/README.md">README</abbr> on how to install Yii.</p>';
}
......
......@@ -5,10 +5,6 @@ return yii\helpers\ArrayHelper::merge(
require(__DIR__ . '/../_config.php'),
[
'components' => [
'fixture' => [
'class' => 'yii\test\DbFixtureManager',
'basePath' => '@tests/unit/fixtures',
],
'db' => [
'dsn' => 'mysql:host=localhost;dbname=yii2_basic_unit',
],
......
......@@ -33,8 +33,8 @@ class LoginFormTest extends TestCase
$this->specify('user should not be able to login with wrong password', function () use ($model) {
$this->assertFalse($model->login());
$this->assertArrayHasKey('password',$model->errors);
$this->assertTrue(Yii::$app->user->isGuest,'user should not be logged in');
$this->assertArrayHasKey('password', $model->errors);
$this->assertTrue(Yii::$app->user->isGuest, 'user should not be logged in');
});
}
......@@ -47,7 +47,7 @@ class LoginFormTest extends TestCase
$this->specify('user should be able to login with correct credentials', function() use ($model) {
$this->assertTrue($model->login());
$this->assertArrayNotHasKey('password',$model->errors);
$this->assertArrayNotHasKey('password', $model->errors);
$this->assertFalse(Yii::$app->user->isGuest,'user should be logged in');
});
}
......@@ -59,4 +59,4 @@ class LoginFormTest extends TestCase
return $loginForm;
}
}
\ No newline at end of file
}
......@@ -3,12 +3,9 @@
namespace tests\unit\models;
use yii\codeception\TestCase;
use yii\test\DbTestTrait;
class UserTest extends TestCase
{
use DbTestTrait;
protected function setUp()
{
parent::setUp();
......
......@@ -10,8 +10,9 @@
defined('YII_DEBUG') or define('YII_DEBUG', true);
// fcgi doesn't have STDIN defined by default
// fcgi doesn't have STDIN and STDOUT defined by default
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
defined('STDIN') or define('STDOUT', fopen('php://stdout', 'w'));
require(__DIR__ . '/vendor/autoload.php');
require(__DIR__ . '/vendor/yiisoft/yii2/Yii.php');
......
......@@ -8,8 +8,9 @@
* @license http://www.yiiframework.com/license/
*/
// fcgi doesn't have STDIN defined by default
// fcgi doesn't have STDIN and STDOUT defined by default
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
defined('STDIN') or define('STDOUT', fopen('php://stdout', 'w'));
define('YII_DEBUG', true);
......
......@@ -298,7 +298,7 @@ class PhpDocController extends Controller
if (isset($prop['get']) && isset($prop['set'])) {
if ($prop['get']['type'] != $prop['set']['type']) {
$note = ' Note that the type of this property differs in getter and setter.'
. ' See [[get'.ucfirst($propName).'()]] and [[set'.ucfirst($propName).'()]] for details.';
. ' See [[get' . ucfirst($propName) . '()]] and [[set' . ucfirst($propName) . '()]] for details.';
}
} elseif (isset($prop['get'])) {
// check if parent class has setter defined
......
......@@ -202,12 +202,14 @@ class Customer extends \yii\db\ActiveRecord
{
public function getOrders()
{
// Customer has_many Order via Order.customer_id -> id
return $this->hasMany(Order::className(), ['customer_id' => 'id']);
}
}
class Order extends \yii\db\ActiveRecord
{
// Order has_one Customer via Customer.id -> customer_id
public function getCustomer()
{
return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
......@@ -413,7 +415,7 @@ and you may also join with sub-relations. For example,
$orders = Order::find()->innerJoinWith([
'books',
'customer' => function ($query) {
$query->where('tbl_customer.create_time > ' . (time() - 24 * 3600));
$query->where('tbl_customer.created_at > ' . (time() - 24 * 3600));
}
])->all();
// join with sub-relations: join with books and books' authors
......
......@@ -57,10 +57,10 @@ class SiteController extends Controller
return [
'access' => [
'class' => \yii\web\AccessControl::className(),
'only' => ['special'],
'only' => ['special-callback'],
'rules' => [
[
'actions' => ['special'],
'actions' => ['special-callback'],
'allow' => true,
'matchCallback' => function ($rule, $action) {
return date('d-m') === '31-10';
......@@ -68,6 +68,17 @@ class SiteController extends Controller
],
```
And the action:
```php
// ...
// Match callback called! This page can be accessed only each October 31st
public function actionSpecialCallback()
{
return $this->render('happy-halloween');
}
```
Sometimes you want a custom action to be taken when access is denied. In this case you can specify `denyCallback`.
Role based access control (RBAC)
......
......@@ -81,8 +81,9 @@ Autoloading
All classes, interfaces and traits are loaded automatically at the moment they are used. There's no need to use
`include` or `require`. It is, as well, true for Composer-loaded packages and Yii extensions.
Autoloader works according to [PSR-4](). That means namespaces and class, interface and trait
names should correspond to file system paths except root namespace path that is defined by an alias.
Autoloader works according to [PSR-4](https://github.com/php-fig/fig-standards/blob/master/proposed/psr-4-autoloader/psr-4-autoloader.md).
That means namespaces and class, interface and trait names should correspond to file system paths except root namespace
path that is defined by an alias.
For example, if standard alias `@app` refers to `/var/www/example.com/` then `\app\models\User` will be loaded from
`/var/www/example.com/app/models/User.php`.
......
......@@ -23,8 +23,8 @@ class User extends ActiveRecord
'timestamp' => [
'class' => 'yii\behaviors\AutoTimestamp',
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => ['create_time', 'update_time'],
ActiveRecord::EVENT_BEFORE_UPDATE => 'update_time',
ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
ActiveRecord::EVENT_BEFORE_UPDATE => 'updated_at',
],
],
];
......@@ -37,20 +37,26 @@ In the above, the `class` value is a string representing the fully qualified beh
Creating your own behaviors
---------------------------
[[NEEDS UPDATING FOR Yii 2]]
To create your own behavior, you must define a class that implements the `IBehavior` interface. This can be accomplished by extending `CBehavior`. More specifically, you can extend `CModelBehavior` or `CActiveRecordBehavior` for behaviors to be used specifically with models or with Active Record models.
To create your own behavior, you must define a class that extends [[yii\base\Behavior]].
```php
class MyBehavior extends CActiveRecordBehavior
namespace app\components;
use yii\base\Behavior;
class MyBehavior extends Behavior
{
}
```
To make your behavior customizable, like `AutoTimestamp`, add public properties:
To make it customizable, like [[yii\behaviors\AutoTimestamp]], add public properties:
```php
class MyBehavior extends CActiveRecordBehavior
namespace app\components;
use yii\base\Behavior;
class MyBehavior extends Behavior
{
public $attr;
}
......@@ -59,6 +65,10 @@ class MyBehavior extends CActiveRecordBehavior
Now, when the behavior is used, you can set the attribute to which you'd want the behavior to be applied:
```php
namespace app\models;
use yii\db\ActiveRecord;
class User extends ActiveRecord
{
// ...
......@@ -67,24 +77,43 @@ class User extends ActiveRecord
{
return [
'mybehavior' => [
'class' => 'ext\mybehavior\MyBehavior',
'class' => 'app\components\MyBehavior',
'attr' => 'member_type'
],
],
];
}
}
```
Behaviors are normally written to take action when certain model-related events occur, such as `beforeSave` or `afterFind`. You can write your behaviors to have the corresponding method. Within the method, you can access the model instance through `$this->getOwner()`:
Behaviors are normally written to take action when certain events occur. Below we're implementing `events` method
to assign event handlers:
```php
class MyBehavior extends CActiveRecordBehavior
namespace app\components;
use yii\base\Behavior;
use yii\db\ActiveRecord;
class MyBehavior extends Behavior
{
public $attr;
public function beforeSave() {
$model = $this->getOwner();
public function events()
{
return [
ActiveRecord::EVENT_BEFORE_INSERT => 'beforeInsert',
ActiveRecord::EVENT_BEFORE_UPDATE => 'beforeUpdate',
];
}
public function beforeInsert() {
$model = $this->owner;
// Use $model->$attr
}
public function beforeUpdate() {
$model = $this->owner;
// Use $model->$attr
}
}
```
\ No newline at end of file
```
......@@ -37,7 +37,7 @@ return [
];
```
This data will be loaded to the `users`, but before it will be loaded table `users` will be cleared: all data deleted, sequence reseted.
This data will be loaded to the `users`, but before it will be loaded table `users` will be cleared: all data deleted, sequence reset.
Above fixture example was auto-generated by `yii2-faker` extension, read more about it in these [section](#auto-generating-fixtures).
Applying fixtures
......
......@@ -41,8 +41,9 @@ code like the following:
defined('YII_DEBUG') or define('YII_DEBUG', true);
// fcgi doesn't have STDIN defined by default
// fcgi doesn't have STDIN and STDOUT defined by default
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
defined('STDIN') or define('STDOUT', fopen('php://stdout', 'w'));
require(__DIR__ . '/vendor/autoload.php');
require(__DIR__ . '/vendor/yiisoft/yii2/Yii.php');
......
......@@ -8,7 +8,7 @@ Most often a controller takes HTTP request data and returns HTML, JSON or XML as
Basics
------
Controller resides in application's `controllers` directory is is named like `SiteController.php` where `Site`
Controller resides in application's `controllers` directory is named like `SiteController.php` where `Site`
part could be anything describing a set of actions it contains.
The basic web controller is a class that extends [[\yii\web\Controller]] and could be very simple:
......@@ -200,7 +200,7 @@ The following code is too simple to implement as a separate action but gives an
can be used in your controller as following:
```php
public SiteController extends \yii\web\Controller
class SiteController extends \yii\web\Controller
{
public function actions()
{
......
......@@ -238,7 +238,7 @@ Using the same `attributes` property you can massively assign data from associat
```php
$attributes = [
'title' => 'Model attributes',
'create_time' => time(),
'created_at' => time(),
];
$post->attributes = $attributes;
```
......
......@@ -29,6 +29,18 @@ on a remote (staging) server, add the parameter allowedIPs to the config to whit
]
```
If you are using `enableStrictParsing` URL manager option, add the following to your `rules`:
```php
'urlManager' => [
'enableStrictParsing' => true,
'rules' => [
// ...
'debug/<controller>/<action>' => 'debug/<controller>/<action>',
],
],
```
How to use it
-------------
......
Fixtures
========
Fixtures are important part of testing. Their main purpose is to set up the environment in a fixed/known state
so that your tests are repeatable and run in an expected way. Yii provides a fixture framework that allows
you to define your fixtures precisely and use them easily.
A key concept in the Yii fixture framework is the so-called *fixture objects*. A fixture object represents
a particular aspect of a test environment and is an instance of [[yii\test\Fixture]] or its child class. For example,
you may use `UserFixture` to make sure the user DB table contains a fixed set of data. You load one or multiple
fixture objects before running a test and unload them when finishing.
A fixture may depend on other fixtures, specified via its [[yii\test\Fixture::depends]] property.
When a fixture is being loaded, the fixtures it depends on will be automatically loaded BEFORE the fixture;
and when the fixture is being unloaded, the dependent fixtures will be unloaded AFTER the fixture.
Defining a Fixture
------------------
To define a fixture, create a new class by extending [[yii\test\Fixture]] or [[yii\test\ActiveFixture]].
The former is best suited for general purpose fixtures, while the latter has enhanced features specifically
designed to work with database and ActiveRecord.
The following code defines a fixture about the `User` ActiveRecord and the corresponding user table.
```php
<?php
namespace app\tests\fixtures;
use yii\test\ActiveFixture;
class UserFixture extends ActiveFixture
{
public $modelClass = 'app\models\User';
}
```
> Tip: Each `ActiveFixture` is about preparing a DB table for testing purpose. You may specify the table
> by setting either the [[yii\test\ActiveFixture::tableName]] property or the [[yii\test\ActiveFixture::modelClass]]
> property. If the latter, the table name will be taken from the `ActiveRecord` class specified by `modelClass`.
The fixture data for an `ActiveFixture` fixture is usually provided in a file located at `FixturePath/data/TableName.php`,
where `FixturePath` stands for the directory containing the fixture class file, and `TableName`
is the name of the table associated with the fixture. In the example above, the file should be
`@app/tests/fixtures/data/tbl_user.php`. The data file should return an array of data rows
to be inserted into the user table. For example,
```php
<?php
return [
'user1' => [
'username' => 'lmayert',
'email' => 'strosin.vernice@jerde.com',
'auth_key' => 'K3nF70it7tzNsHddEiq0BZ0i-OU8S3xV',
'password' => '$2y$13$WSyE5hHsG1rWN2jV8LRHzubilrCLI5Ev/iK0r3jRuwQEs2ldRu.a2',
],
'user2' => [
'username' => 'napoleon69',
'email' => 'aileen.barton@heaneyschumm.com',
'auth_key' => 'dZlXsVnIDgIzFgX4EduAqkEPuphhOh9q',
'password' => '$2y$13$kkgpvJ8lnjKo8RuoR30ay.RjDf15bMcHIF7Vz1zz/6viYG5xJExU6',
],
];
```
You may give an alias to a row so that later in your test, you may refer to the row via the alias. In the above example,
the two rows are aliased as `user1` and `user2`, respectively.
Also, you do not need to specify the data for auto-incremental columns. Yii will automatically fill the actual
values into the rows when the fixture is being loaded.
> Tip: You may customize the location of the data file by setting the [[yii\test\ActiveFixture::dataFile]] property.
> You may also override [[yii\test\ActiveFixture::getData()]] to provide the data.
As we described earlier, a fixture may depend on other fixtures. For example, `UserProfileFixture` depends on `UserFixture`
because the user profile table contains a foreign key pointing to the user table.
The dependency is specified via the [[yii\test\Fixture::depends]] property, like the following,
```php
namespace app\tests\fixtures;
use yii\test\ActiveFixture;
class UserProfileFixture extends ActiveFixture
{
public $modelClass = 'app\models\UserProfile';
public $depends = ['app\tests\fixtures\UserFixture'];
}
In the above, we have shown how to define a fixture about a DB table. To define a fixture not related with DB
(e.g. a fixture about certain files and directories), you may extend from the more general base class
[[yii\test\Fixture]] and override the [[yii\test\Fixture::load()|load()]] and [[yii\test\Fixture::unload()|unload()]] methods.
Using Fixtures
--------------
If you are using [CodeCeption](http://codeception.com/) to test your code, you should consider using
the `yii2-codeception` extension which has the built-in support for loading and accessing fixtures.
If you are using other testing frameworks, you may use [[yii\test\FixtureTrait]] in your test cases
to achieve the same goal.
In the following we will describe how to write a `UserProfile` unit test class using `yii2-codeception`.
In your unit test class extending [[yii\codeception\DbTestCase]] or [[yii\codeception\TestCase]],
declare which fixtures you want to use in the [[yii\testFixtureTrait::fixtures()|fixtures()]] method. For example,
```php
namespace app\tests\unit\models;
use yii\codeception\DbTestCase;
use app\tests\fixtures\UserProfileFixture;
class UserProfileTest extends DbTestCase
{
protected function fixtures()
{
return [
'profiles' => UserProfileFixture::className(),
];
}
// ...test methods...
}
```
The fixtures listed in the `fixtures()` method will be automatically loaded before running every test method
in the test case and unloaded after finishing every test method. And as we described before, when a fixture is
being loaded, all its dependent fixtures will be automatically loaded first. In the above example, because
`UserProfileFixture` depends on `UserFixture`, when running any test method in the test class,
two fixtures will be loaded sequentially: `UserFixture` and `UserProfileFixture`.
When specifying fixtures in `fixtures()`, you may use either a class name or a configuration array to refer to
a fixture. The configuration array will let you customize the fixture properties when the fixture is loaded.
You may also assign an alias to a fixture. In the above example, the `UserProfileFixture` is aliased as `profiles`.
In the test methods, you may then access a fixture object using its alias. For example, `$this->profiles` will
return the `UserProfileFixture` object.
Because `UserProfileFixture` extends from `ActiveFixture`, you may further use the following syntax to access
the data provided by the fixture:
```php
// returns the data row aliased as 'user1'
$row = $this->profiles['user1'];
// returns the UserProfile model corresponding to the data row aliased as 'user1'
$profile = $this->profiles('user1');
// traverse every data row in the fixture
foreach ($this->profiles as $row) ...
```
> Info: `$this->profiles` is still of `UserProfileFixture` type. The above access features are implemented
> through PHP magic methods.
Defining and Using Global Fixtures
----------------------------------
The fixtures described above are mainly used by individual test cases. In most cases, you also need some global
fixtures that are applied to ALL or many test cases. An example is [[yii\test\InitDbFixture]] which does
two things:
* Perform some common initialization tasks by executing a script located at `@app/tests/fixtures/initdb.php`;
* Disable the database integrity check before loading other DB fixtures, and re-enable it after other DB fixtures are unloead.
Using global fixtures is similar to using non-global ones. The only difference is that you declare these fixtures
in [[yii\codeception\TestCase::globalFixtures()]] instead of `fixtures()`. When a test case loads fixtures, it will
first load global fixtures and then non-global ones.
By default, [[yii\codeception\DbTestCase]] already declares `InitDbFixture` in its `globalFixtures()` method.
This means you only need to work with `@app/tests/fixtures/initdb.php` if you want to do some initialization work
before each test. You may otherwise simply focus on developing each individual test case and the corresponding fixtures.
Summary
-------
In the above, we have described how to define and use fixtures. Below we summarize the typical workflow
of running unit tests related with DB:
1. Use `yii migrate` tool to upgrade your test database to the latest version;
2. Run a test case:
- Load fixtures: clean up the relevant DB tables and populate them with fixture data;
- Perform the actual test;
- Unload fixtures.
3. Repeat 2 until all tests finish.
......@@ -119,7 +119,7 @@ needs to be divisible by 10. In the rules you would define: `['attributeName', '
Then, your own method could look like this:
```php
public function myValidationMethod($attribute) {
if(($attribute % 10) != 0) {
if(($this->$attribute % 10) != 0) {
$this->addError($attribute, 'cannot divide value by 10');
}
}
......
Creating issues
Report an Issue
===============
You got into rough corner while working with yii, or you found a bug? We are very sorry for that, but we can sort that
out together.
Please follow the guidelines below when creating an issue so that your issue can be more promptly resolved:
- If you are unsure about a function, you may ask on IRC or the forums. If the documentation is unclear, open a separate
issue.
- Please use English if possible.
- Make sure it is clear what is the problem and how to reproduce it.
* Provide information including: the version of PHP and Yii, the type of operating system and Web server, browser type and version;
* Provide the **complete** error call stack if available. A screenshot to explain the issue is very welcome.
* Describe the steps for reproducing the issue. It would be even better if you could provide code to reproduce the issue.
If you are going to report security issue please **do not** use the issue tracker and instead
[contact us directly](http://www.yiiframework.com/security/).
**Do not report an issue if**
* you are asking how to use some Yii feature. You should use [the forum](http://www.yiiframework.com/forum/index.php/forum/42-general-discussions-for-yii-20/) or [chat room](http://www.yiiframework.com/chat/) for this purpose.
* your issue is about security. Please [contact us directly](http://www.yiiframework.com/security/) to report security issues.
**Avoid duplicated issues**
Before you report an issue, please search through [existing issues](https://github.com/yiisoft/yii2/issues) to see if your issue is already reported or fixed to make sure you are not reporting a duplicated issue. Also make sure you have the latest version of Yii and see if the issue still exists.
......@@ -84,22 +84,22 @@ class BaseDoc extends Object
// TODO
public function loadSource($reflection)
{
$this->sourcePath=str_replace('\\','/',str_replace(YII_PATH,'',$reflection->getFileName()));
$this->startLine=$reflection->getStartLine();
$this->endLine=$reflection->getEndLine();
$this->sourcePath = str_replace('\\', '/', str_replace(YII_PATH, '', $reflection->getFileName()));
$this->startLine = $reflection->getStartLine();
$this->endLine = $reflection->getEndLine();
}
public function getSourceUrl($baseUrl,$line=null)
public function getSourceUrl($baseUrl, $line=null)
{
if($line===null)
return $baseUrl.$this->sourcePath;
if($line === null)
return $baseUrl . $this->sourcePath;
else
return $baseUrl.$this->sourcePath.'#'.$line;
return $baseUrl . $this->sourcePath . '#' . $line;
}
public function getSourceCode()
{
$lines=file(YII_PATH.$this->sourcePath);
return implode("",array_slice($lines,$this->startLine-1,$this->endLine-$this->startLine+1));
$lines = file(YII_PATH . $this->sourcePath);
return implode("", array_slice($lines, $this->startLine - 1, $this->endLine - $this->startLine + 1));
}
}
\ No newline at end of file
}
......@@ -239,7 +239,7 @@ abstract class Renderer extends BaseRenderer implements ViewContextInterface
break;
}
}
return implode(" &raquo;\n",$parents);
return implode(" &raquo;\n", $parents);
}
/**
......@@ -257,7 +257,7 @@ abstract class Renderer extends BaseRenderer implements ViewContextInterface
$interfaces[] = $interface; // TODO link to php.net
}
}
return implode(', ',$interfaces);
return implode(', ', $interfaces);
}
/**
......@@ -275,7 +275,7 @@ abstract class Renderer extends BaseRenderer implements ViewContextInterface
$traits[] = $trait; // TODO link to php.net
}
}
return implode(', ',$traits);
return implode(', ', $traits);
}
/**
......@@ -293,7 +293,7 @@ abstract class Renderer extends BaseRenderer implements ViewContextInterface
$classes[] = $class; // TODO link to php.net
}
}
return implode(', ',$classes);
return implode(', ', $classes);
}
/**
......@@ -350,4 +350,4 @@ abstract class Renderer extends BaseRenderer implements ViewContextInterface
{
return Yii::getAlias('@yii/apidoc/templates/html/views/' . $view);
}
}
\ No newline at end of file
}
......@@ -173,10 +173,10 @@ abstract class BaseOAuth extends BaseClient implements ClientInterface
$curlOptions = $this->mergeCurlOptions(
$this->defaultCurlOptions(),
$this->getCurlOptions(),
array(
[
CURLOPT_RETURNTRANSFER => true,
CURLOPT_URL => $url,
),
],
$this->composeRequestCurlOptions(strtoupper($method), $url, $params)
);
$curlResource = curl_init();
......@@ -217,7 +217,7 @@ abstract class BaseOAuth extends BaseClient implements ClientInterface
while (!empty($args)) {
$next = array_shift($args);
foreach ($next as $k => $v) {
$res[$k]=$v;
$res[$k] = $v;
}
}
return $res;
......@@ -507,4 +507,4 @@ abstract class BaseOAuth extends BaseClient implements ClientInterface
* @throws Exception on failure.
*/
abstract protected function apiInternal($accessToken, $url, $method, array $params);
}
\ No newline at end of file
}
Yii Framework 2 Codeception extension Change Log
================================================
- Enh: yii\codeception\TestCase now supports loading and using fixtures via Yii fixture framework (qiangxue)
- New #1956: Implemented test fixture framework (qiangxue)
- New: Added yii\codeception\DbTestCase (qiangxue)
2.0.0 beta under development
----------------------------
- Initial release.
\ No newline at end of file
- Initial release.
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\codeception;
use yii\test\InitDbFixture;
/**
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class DbTestCase extends TestCase
{
/**
* @inheritdoc
*/
protected function globalFixtures()
{
return [
InitDbFixture::className(),
];
}
}
......@@ -5,6 +5,7 @@ namespace yii\codeception;
use Yii;
use yii\base\InvalidConfigException;
use Codeception\TestCase\Test;
use yii\test\FixtureTrait;
/**
* TestCase is the base class for all codeception unit tests
......@@ -14,6 +15,8 @@ use Codeception\TestCase\Test;
*/
class TestCase extends Test
{
use FixtureTrait;
/**
* @var array|string the application configuration that will be used for creating an application instance for each test.
* You can use a string to represent the file path or path alias of a configuration file.
......@@ -29,6 +32,7 @@ class TestCase extends Test
{
parent::setUp();
$this->mockApplication();
$this->loadFixtures();
}
/**
......@@ -36,6 +40,7 @@ class TestCase extends Test
*/
protected function tearDown()
{
$this->unloadFixtures();
$this->destroyApplication();
parent::tearDown();
}
......
......@@ -28,7 +28,7 @@ echo GridView::widget([
'value' => function ($data) {
$timeInSeconds = $data['time'] / 1000;
$millisecondsDiff = (int)(($timeInSeconds - (int)$timeInSeconds) * 1000);
return date('H:i:s.',$timeInSeconds) . sprintf('%03d',$millisecondsDiff);
return date('H:i:s.', $timeInSeconds) . sprintf('%03d', $millisecondsDiff);
},
'headerOptions' => [
'class' => 'sort-numerical'
......@@ -70,4 +70,4 @@ echo GridView::widget([
],
],
]);
?>
\ No newline at end of file
?>
......@@ -28,7 +28,7 @@ echo GridView::widget([
[
'attribute' => 'duration',
'value' => function ($data) {
return sprintf('%.1f ms',$data['duration']);
return sprintf('%.1f ms', $data['duration']);
},
'options' => [
'width' => '10%',
......
......@@ -367,7 +367,7 @@ class ActiveRecord extends BaseActiveRecord
* For example, to change the status to be 1 for all customers whose status is 2:
*
* ~~~
* Customer::updateAll(array('status' => 1), array(2, 3, 4));
* Customer::updateAll(['status' => 1], [2, 3, 4]);
* ~~~
*
* @param array $attributes attribute values (name-value pairs) to be saved into the table
......
......@@ -138,7 +138,7 @@ class QueryBuilder extends \yii\base\Object
*/
public function buildCondition($condition)
{
static $builders = array(
static $builders = [
'not' => 'buildNotCondition',
'and' => 'buildAndCondition',
'or' => 'buildAndCondition',
......@@ -150,7 +150,7 @@ class QueryBuilder extends \yii\base\Object
'not like' => 'buildLikeCondition',
'or like' => 'buildLikeCondition',
'or not like' => 'buildLikeCondition',
);
];
if (empty($condition)) {
return [];
......
......@@ -89,7 +89,7 @@ class Customer extends \yii\elasticsearch\ActiveRecord
*/
public static function active($query)
{
$query->andWhere(array('status' => 1));
$query->andWhere(['status' => 1]);
}
}
```
......
<?php
namespace yii\mongodb;
use Yii;
use yii\base\InvalidConfigException;
class ActiveFixture extends \yii\test\BaseActiveFixture
{
/**
* @var Connection|string the DB connection object or the application component ID of the DB connection.
*/
public $db = 'mongodb';
/**
* @var string|array the collection name that this fixture is about. If this property is not set,
* the table name will be determined via [[modelClass]].
* @see [[yii\mongodb\Connection::getCollection()]]
*/
public $collectionName;
/**
* @inheritdoc
*/
public function init()
{
parent::init();
if (!isset($this->modelClass) && !isset($this->collectionName)) {
throw new InvalidConfigException('Either "modelClass" or "collectionName" must be set.');
}
}
/**
* Loads the fixture data.
* The default implementation will first reset the DB table and then populate it with the data
* returned by [[getData()]].
*/
public function load()
{
$this->resetCollection();
$data = $this->getData();
$this->getCollection()->batchInsert($data);
foreach ($data as $alias => $row) {
$this->data[$alias] = $row;
}
}
protected function getCollection()
{
return $this->db->getCollection($this->getCollectionName());
}
protected function getCollectionName()
{
if ($this->collectionName) {
return $this->collectionName;
} else {
$modelClass = $this->modelClass;
return $modelClass::collectionName();
}
}
/**
* Returns the fixture data.
*
* This method is called by [[loadData()]] to get the needed fixture data.
*
* The default implementation will try to return the fixture data by including the external file specified by [[dataFile]].
* The file should return an array of data rows (column name => column value), each corresponding to a row in the table.
*
* If the data file does not exist, an empty array will be returned.
*
* @return array the data rows to be inserted into the collection.
*/
protected function getData()
{
if ($this->dataFile === false) {
return [];
}
if ($this->dataFile !== null) {
$dataFile = Yii::getAlias($this->dataFile);
} else {
$class = new \ReflectionClass($this);
$dataFile = dirname($class->getFileName()) . '/data/' . $this->getCollectionName() . '.php';
}
return is_file($dataFile) ? require($dataFile) : [];
}
/**
* Removes all existing data from the specified collection and resets sequence number if any.
* This method is called before populating fixture data into the collection associated with this fixture.
*/
protected function resetCollection()
{
$this->getCollection()->remove();
}
}
\ No newline at end of file
......@@ -21,10 +21,10 @@ Yii Framework 2 Change Log
- Bug #1686: ActiveForm is creating duplicated messages in error summary (qiangxue)
- Bug #1704: Incorrect regexp is used in `Inflector::camelize()` (qiangxue)
- Bug #1710: OpenId auth client does not request required attributes correctly (klimov-paul)
- Bug #1733: Incorrect code about `$_modelClasses` in `DbFixtureManager` (qiangxue)
- Bug #1798: Fixed label attributes for array fields (zhuravljov)
- Bug #1800: Better check for `$_SERVER['HTTPS']` in `yii\web\Request::getIsSecureConnection()` (ginus, samdark)
- Bug #1827: Debugger toolbar is loaded twice if an action is calling `run()` to execute another action (qiangxue)
- Bug #1868: Added ability to exclude tables from FixtureController apply/clear actions. (Ragazzo)
- Bug #1869: Fixed tables clearing. `TRUNCATE` changed to `DELETE` to avoid postgresql tables checks (and truncating all tables) (Ragazzo)
- Bug #1870: Validation errors weren't properly translated when using clientside validation (samdark)
- Bug #1930: Fixed domain based URL matching for website root (samdark)
......@@ -40,6 +40,7 @@ Yii Framework 2 Change Log
- Bug: Fixed issue with tabular input on ActiveField::radio() and ActiveField::checkbox() (jom)
- Bug: Fixed the issue that query cache returns the same data for the same SQL but different query methods (qiangxue)
- Bug: Fixed URL parsing so it's now properly giving 404 for URLs like `http://example.com//////site/about` (samdark)
- Bug: Fixed `HelpController::getModuleCommands` issue where it attempts to scan a module's controller directory when it doesn't exist (jom)
- Enh #46: Added Image extension based on [Imagine library](http://imagine.readthedocs.org) (tonydspaniard)
- Enh #364: Improve Inflector::slug with `intl` transliteration. Improved transliteration char map. (tonydspaniard)
- Enh #797: Added support for validating multiple columns by `UniqueValidator` and `ExistValidator` (qiangxue)
......@@ -72,6 +73,8 @@ Yii Framework 2 Change Log
- Enh #1973: `yii message/extract` is now able to generate `.po` files (SergeiKutanov, samdark)
- Enh #1984: ActionFilter will now mark event as handled when action run is aborted (cebe)
- Enh #2003: Added `filter` property to `ExistValidator` and `UniqueValidator` to support adding additional filtering conditions (qiangxue)
- Enh #2043: Added support for custom request body parsers (danschmidt5189, cebe)
- Enh #2051: Do not save null data into database when using RBAC (qiangxue)
- Enh: Added `favicon.ico` and `robots.txt` to default application templates (samdark)
- Enh: Added `Widget::autoIdPrefix` to support prefixing automatically generated widget IDs (qiangxue)
- Enh: Support for file aliases in console command 'message' (omnilight)
......@@ -82,6 +85,8 @@ Yii Framework 2 Change Log
- Enh: `init` of advanced application now allows to specify answer for overwriting files via `init --overwrite=n` (samdark)
- Enh: Added `TableSchema::fullName` property (qiangxue)
- Enh #1839: Added support for getting file extension and basename from uploaded file (anfrantic)
- Enh: yii\codeception\TestCase now supports loading and using fixtures via Yii fixture framework (qiangxue)
- Enh: Added support to parse json request data to Request::getRestParams() (cebe)
- Chg #1519: `yii\web\User::loginRequired()` now returns the `Response` object instead of exiting the application (qiangxue)
- Chg #1586: `QueryBuilder::buildLikeCondition()` will now escape special characters and use percentage characters by default (qiangxue)
- Chg #1610: `Html::activeCheckboxList()` and `Html::activeRadioList()` will submit an empty string if no checkbox/radio is selected (qiangxue)
......@@ -91,6 +96,8 @@ Yii Framework 2 Change Log
- Chg #1821: Changed default values for yii\db\Connection username and password to null (cebe)
- Chg #1844: `Response::sendFile()` and other file sending methods will not send the response (qiangxue)
- Chg #1852: DbConnection::tablePrefix default value now 'tbl_' (creocoder)
- Chg #2057: AutoTimestamp attributes defaults changed from `create_time` and `update_time` to `created_at` and `updated_at` (creocoder)
- Chg #2063: Removed `yii\web\Request::acceptTypes` and renamed `yii\web\Request::acceptedContentTypes` to `acceptableContentTypes` (qiangxue)
- Chg: Renamed `yii\jui\Widget::clientEventsMap` to `clientEventMap` (qiangxue)
- Chg: Renamed `ActiveRecord::getPopulatedRelations()` to `getRelatedRecords()` (qiangxue)
- Chg: Renamed `attributeName` and `className` to `targetAttribute` and `targetClass` for `UniqueValidator` and `ExistValidator` (qiangxue)
......@@ -101,10 +108,14 @@ Yii Framework 2 Change Log
- Chg: Changed the directory structure according to PSR-4. You have to update your application `index.php`,
`index-test.php` and `yii` files to point to the new location of `Yii.php` (qiangxue, cebe)
- Chg: Advanced app template: moved database connection DSN, login and password to `-local` config not to expose it to VCS (samdark)
- Chg: Renamed `yii\web\Request::acceptedLanguages` to `acceptableLanguages` (qiangxue)
- New #66: [Auth client library](https://github.com/yiisoft/yii2-authclient) OpenId, OAuth1, OAuth2 clients (klimov-paul)
- New #1393: [Codeception testing framework integration](https://github.com/yiisoft/yii2-codeception) (Ragazzo)
- New #1438: [MongoDB integration](https://github.com/yiisoft/yii2-mongodb) ActiveRecord and Query (klimov-paul)
- New #1956: Implemented test fixture framework (qiangxue)
- New: Yii framework now comes with core messages in multiple languages
- New: Added yii\codeception\DbTestCase (qiangxue)
2.0.0 alpha, December 1, 2013
---------------------------
......
......@@ -335,4 +335,4 @@ $.fn.extend({
});
})(jQuery);
\ No newline at end of file
})(jQuery);
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\base;
/**
* ArrayAccessTrait provides the implementation for `IteratorAggregate`, `ArrayAccess` and `Countable`.
*
* Note that ArrayAccessTrait requires the class using it contain a property named `data` which should be an array.
* The data will be exposed by ArrayAccessTrait to support accessing the class object like an array.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
trait ArrayAccessTrait
{
/**
* Returns an iterator for traversing the data.
* This method is required by the SPL interface `IteratorAggregate`.
* It will be implicitly called when you use `foreach` to traverse the collection.
* @return \ArrayIterator an iterator for traversing the cookies in the collection.
*/
public function getIterator()
{
return new \ArrayIterator($this->data);
}
/**
* Returns the number of data items.
* This method is required by Countable interface.
* @return integer number of data elements.
*/
public function count()
{
return count($this->data);
}
/**
* This method is required by the interface ArrayAccess.
* @param mixed $offset the offset to check on
* @return boolean
*/
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
/**
* This method is required by the interface ArrayAccess.
* @param integer $offset the offset to retrieve element.
* @return mixed the element at the offset, null if no element is found at the offset
*/
public function offsetGet($offset)
{
return isset($this->data[$offset]) ? $this->data[$offset] : null;
}
/**
* This method is required by the interface ArrayAccess.
* @param integer $offset the offset to set element
* @param mixed $item the element value
*/
public function offsetSet($offset, $item)
{
$this->data[$offset] = $item;
}
/**
* This method is required by the interface ArrayAccess.
* @param mixed $offset the offset to unset element
*/
public function offsetUnset($offset)
{
unset($this->data[$offset]);
}
}
......@@ -289,7 +289,7 @@ class ErrorHandler extends Component
*/
public function createHttpStatusLink($statusCode, $statusDescription)
{
return '<a href="http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#' . (int)$statusCode .'" target="_blank">HTTP ' . (int)$statusCode . ' &ndash; ' . $statusDescription . '</a>';
return '<a href="http://en.wikipedia.org/wiki/List_of_HTTP_status_codes#' . (int)$statusCode . '" target="_blank">HTTP ' . (int)$statusCode . ' &ndash; ' . $statusDescription . '</a>';
}
/**
......
......@@ -34,7 +34,7 @@ use Yii;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
abstract class Module extends Component
class Module extends Component
{
/**
* @var array custom module parameters (name => value).
......
......@@ -27,8 +27,8 @@ use yii\db\ActiveRecord;
* }
* ~~~
*
* By default, AutoTimestamp will fill the `create_time` attribute with the current timestamp
* when the associated AR object is being inserted; it will fill the `update_time` attribute
* By default, AutoTimestamp will fill the `created_at` attribute with the current timestamp
* when the associated AR object is being inserted; it will fill the `updated_at` attribute
* with the timestamp when the AR object is being updated.
*
* @author Qiang Xue <qiang.xue@gmail.com>
......@@ -41,12 +41,12 @@ class AutoTimestamp extends Behavior
* The array keys are the ActiveRecord events upon which the attributes are to be filled with timestamps,
* and the array values are the corresponding attribute(s) to be updated. You can use a string to represent
* a single attribute, or an array to represent a list of attributes.
* The default setting is to update the `create_time` attribute upon AR insertion,
* and update the `update_time` attribute upon AR updating.
* The default setting is to update the `created_at` attribute upon AR insertion,
* and update the `updated_at` attribute upon AR updating.
*/
public $attributes = [
ActiveRecord::EVENT_BEFORE_INSERT => 'create_time',
ActiveRecord::EVENT_BEFORE_UPDATE => 'update_time',
ActiveRecord::EVENT_BEFORE_INSERT => 'created_at',
ActiveRecord::EVENT_BEFORE_UPDATE => 'updated_at',
];
/**
* @var \Closure|Expression The expression that will be used for generating the timestamp.
......
......@@ -174,8 +174,6 @@ return [
'yii\rbac\Manager' => YII_PATH . '/rbac/Manager.php',
'yii\rbac\PhpManager' => YII_PATH . '/rbac/PhpManager.php',
'yii\requirements\YiiRequirementChecker' => YII_PATH . '/requirements/YiiRequirementChecker.php',
'yii\test\DbFixtureManager' => YII_PATH . '/test/DbFixtureManager.php',
'yii\test\DbTestTrait' => YII_PATH . '/test/DbTestTrait.php',
'yii\validators\BooleanValidator' => YII_PATH . '/validators/BooleanValidator.php',
'yii\validators\CompareValidator' => YII_PATH . '/validators/CompareValidator.php',
'yii\validators\DateValidator' => YII_PATH . '/validators/DateValidator.php',
......
......@@ -381,9 +381,11 @@ class AssetController extends Controller
{
$array = [];
foreach ($targets as $name => $target) {
foreach (['js', 'css', 'depends', 'basePath', 'baseUrl'] as $prop) {
foreach (['basePath', 'baseUrl', 'js', 'css', 'depends'] as $prop) {
if (!empty($target->$prop)) {
$array[$name][$prop] = $target->$prop;
} elseif (in_array($prop, ['js', 'css'])) {
$array[$name][$prop] = [];
}
}
}
......@@ -556,7 +558,7 @@ EOD;
return str_replace($inputUrl, $outputUrl, $fullMatch);
};
$cssContent = preg_replace_callback('/url\(["\']?([^"]*)["\']?\)/is', $callback, $cssContent);
$cssContent = preg_replace_callback('/url\(["\']?([^)^"^\']*)["\']?\)/is', $callback, $cssContent);
return $cssContent;
}
......
......@@ -11,7 +11,6 @@ use Yii;
use yii\console\Controller;
use yii\console\Exception;
use yii\helpers\FileHelper;
use yii\test\DbTestTrait;
use yii\helpers\Console;
/**
......@@ -110,7 +109,7 @@ class FixtureController extends Controller
* @param array $fixtures
* @throws \yii\console\Exception
*/
public function actionApply(array $fixtures)
public function actionApply(array $fixtures, array $except = [])
{
if ($this->getFixtureManager() === null) {
throw new Exception('Fixture manager is not configured properly. Please refer to official documentation for this purposes.');
......@@ -132,10 +131,12 @@ class FixtureController extends Controller
);
}
if (!$this->confirmApply($foundFixtures)) {
if (!$this->confirmApply($foundFixtures, $except)) {
return;
}
$fixtures = array_diff($foundFixtures, $except);
$this->getFixtureManager()->basePath = $this->fixturePath;
$this->getFixtureManager()->db = $this->db;
......@@ -159,16 +160,18 @@ class FixtureController extends Controller
* whitespace between tables names.
* @param array|string $tables
*/
public function actionClear(array $tables)
public function actionClear(array $tables, array $except = ['tbl_migration'])
{
if ($this->needToApplyAll($tables[0])) {
$tables = $this->getDbConnection()->schema->getTableNames();
}
if (!$this->confirmClear($tables)) {
if (!$this->confirmClear($tables, $except)) {
return;
}
$tables = array_diff($tables, $except);
$transaction = Yii::$app->db->beginTransaction();
try {
......@@ -246,26 +249,40 @@ class FixtureController extends Controller
/**
* Prompts user with confirmation if fixtures should be loaded.
* @param array $fixtures
* @param array $except
* @return boolean
*/
private function confirmApply($fixtures)
private function confirmApply($fixtures, $except)
{
$this->stdout("Fixtures will be loaded from path: \n", Console::FG_YELLOW);
$this->stdout(Yii::getAlias($this->fixturePath) . "\n\n", Console::FG_GREEN);
$this->outputList($fixtures);
return $this->confirm('Load to database above fixtures?');
if (count($except)) {
$this->stdout("\nFixtures that will NOT be loaded: \n\n", Console::FG_YELLOW);
$this->outputList($except);
}
return $this->confirm("\nLoad to database above fixtures?");
}
/**
* Prompts user with confirmation for tables that should be cleared.
* @param array $tables
* @param array $except
* @return boolean
*/
private function confirmClear($tables)
private function confirmClear($tables, $except)
{
$this->stdout("Tables below will be cleared:\n\n", Console::FG_YELLOW);
$this->outputList($tables);
return $this->confirm('Clear tables?');
if (count($except)) {
$this->stdout("\nTables that will NOT be cleared:\n\n", Console::FG_YELLOW);
$this->outputList($except);
}
return $this->confirm("\nClear tables?");
}
/**
......@@ -291,7 +308,7 @@ class FixtureController extends Controller
/**
* @param array $fixtures
* @return array Array of found fixtures. These may differer from input parameter as not all fixtures may exists.
* @return array Array of found fixtures. These may differ from input parameter as not all fixtures may exists.
*/
private function findFixtures(array $fixtures)
{
......
......@@ -123,10 +123,13 @@ class HelpController extends Controller
}
}
$files = scandir($module->getControllerPath());
foreach ($files as $file) {
if (strcmp(substr($file, -14), 'Controller.php') === 0) {
$commands[] = $prefix . Inflector::camel2id(substr(basename($file), 0, -14));
$controllerPath = $module->getControllerPath();
if (is_dir($controllerPath)) {
$files = scandir($controllerPath);
foreach ($files as $file) {
if (strcmp(substr($file, -14), 'Controller.php') === 0) {
$commands[] = $prefix . Inflector::camel2id(substr(basename($file), 0, -14));
}
}
}
......
......@@ -86,6 +86,8 @@ class Pagination extends Object
* @var array parameters (name => value) that should be used to obtain the current page number
* and to create new pagination URLs. If not set, all parameters from $_GET will be used instead.
*
* In order to add hash to all links use `array_merge($_GET, ['#' => 'my-hash'])`.
*
* The array element indexed by [[pageVar]] is considered to be the current page number.
* If the element does not exist, the current page number is considered 0.
*/
......
......@@ -145,7 +145,7 @@ class Sort extends Object
* ~~~
* [
* 'name' => SORT_ASC,
* 'create_time' => SORT_DESC,
* 'created_at' => SORT_DESC,
* ]
* ~~~
*
......@@ -168,6 +168,8 @@ class Sort extends Object
* @var array parameters (name => value) that should be used to obtain the current sort directions
* and to create new sort URLs. If not set, $_GET will be used instead.
*
* In order to add hash to all links use `array_merge($_GET, ['#' => 'my-hash'])`.
*
* The array element indexed by [[sortVar]] is considered to be the current sort directions.
* If the element does not exist, the [[defaults|default order]] will be used.
*
......
......@@ -333,7 +333,7 @@ class Migration extends \yii\base\Component
*/
public function addPrimaryKey($name, $table, $columns)
{
echo " > add primary key $name on $table (".(is_array($columns) ? implode(',', $columns) : $columns).") ...";
echo " > add primary key $name on $table (" . (is_array($columns) ? implode(',', $columns) : $columns).") ...";
$time = microtime(true);
$this->db->createCommand()->addPrimaryKey($name, $table, $columns)->execute();
echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
......@@ -343,7 +343,6 @@ class Migration extends \yii\base\Component
* Builds and executes a SQL statement for dropping a primary key.
* @param string $name the name of the primary key constraint to be removed.
* @param string $table the table that the primary key constraint will be removed from.
* @return Command the command object itself
*/
public function dropPrimaryKey($name, $table)
{
......@@ -358,15 +357,15 @@ class Migration extends \yii\base\Component
* The method will properly quote the table and column names.
* @param string $name the name of the foreign key constraint.
* @param string $table the table that the foreign key constraint will be added to.
* @param string $columns the name of the column to that the constraint will be added on. If there are multiple columns, separate them with commas.
* @param string $columns the name of the column to that the constraint will be added on. If there are multiple columns, separate them with commas or use an array.
* @param string $refTable the table that the foreign key references to.
* @param string $refColumns the name of the column that the foreign key references to. If there are multiple columns, separate them with commas.
* @param string $refColumns the name of the column that the foreign key references to. If there are multiple columns, separate them with commas or use an array.
* @param string $delete the ON DELETE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL
* @param string $update the ON UPDATE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL
*/
public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null)
{
echo " > add foreign key $name: $table ($columns) references $refTable ($refColumns) ...";
echo " > add foreign key $name: $table (" . implode(',', (array)$columns) . ") references $refTable (" . implode(',', (array)$refColumns) . ") ...";
$time = microtime(true);
$this->db->createCommand()->addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete, $update)->execute();
echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
......@@ -390,12 +389,12 @@ class Migration extends \yii\base\Component
* @param string $name the name of the index. The name will be properly quoted by the method.
* @param string $table the table that the new index will be created for. The table name will be properly quoted by the method.
* @param string $column the column(s) that should be included in the index. If there are multiple columns, please separate them
* by commas. The column names will be properly quoted by the method.
* by commas or use an array. The column names will be properly quoted by the method.
* @param boolean $unique whether to add UNIQUE constraint on the created index.
*/
public function createIndex($name, $table, $column, $unique = false)
{
echo " > create" . ($unique ? ' unique' : '') . " index $name on $table ($column) ...";
echo " > create" . ($unique ? ' unique' : '') . " index $name on $table (" . implode(',', (array)$column) . ") ...";
$time = microtime(true);
$this->db->createCommand()->createIndex($name, $table, $column, $unique)->execute();
echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
......
......@@ -93,8 +93,8 @@ class QueryBuilder extends \yii\db\QueryBuilder
*/
public function alterColumn($table, $column, $type)
{
$type=$this->getColumnType($type);
$sql='ALTER TABLE ' . $this->db->quoteTableName($table) . ' ALTER COLUMN '
$type = $this->getColumnType($type);
$sql = 'ALTER TABLE ' . $this->db->quoteTableName($table) . ' ALTER COLUMN '
. $this->db->quoteColumnName($column) . ' '
. $this->getColumnType($type);
return $sql;
......
......@@ -33,7 +33,7 @@ class BaseArrayHelper
* 'id',
* 'title',
* // the key name in array result => property name
* 'createTime' => 'create_time',
* 'createTime' => 'created_at',
* // the key name in array result => anonymous function
* 'length' => function ($post) {
* return strlen($post->content);
......
......@@ -146,7 +146,7 @@ class DbManager extends Manager
}
$query = new Query;
$rows = $query->from($this->itemTable)
->where(['or', 'name=:name1', 'name=:name2'], [':name1' => $itemName, ':name2' => $childName])
->where(['or', 'name=:name1', 'name=:name2'], [':name1' => $itemName, ':name2' => $childName])
->createCommand($this->db)
->queryAll();
if (count($rows) == 2) {
......@@ -216,7 +216,7 @@ class DbManager extends Manager
->queryAll();
$children = [];
foreach ($rows as $row) {
if (($data = @unserialize($row['data'])) === false) {
if (!isset($row['data']) || ($data = @unserialize($row['data'])) === false) {
$data = null;
}
$children[$row['name']] = new Item([
......@@ -251,7 +251,7 @@ class DbManager extends Manager
'user_id' => $userId,
'item_name' => $itemName,
'biz_rule' => $bizRule,
'data' => serialize($data),
'data' => $data === null ? null : serialize($data),
])
->execute();
return new Assignment([
......@@ -299,7 +299,7 @@ class DbManager extends Manager
$query = new Query;
return $query->select(['item_name'])
->from($this->assignmentTable)
->where(['user_id' => $userId, 'item_name' => $itemName])
->where(['user_id' => $userId, 'item_name' => $itemName])
->createCommand($this->db)
->queryScalar() !== false;
}
......@@ -315,11 +315,11 @@ class DbManager extends Manager
{
$query = new Query;
$row = $query->from($this->assignmentTable)
->where(['user_id' => $userId, 'item_name' => $itemName])
->where(['user_id' => $userId, 'item_name' => $itemName])
->createCommand($this->db)
->queryOne();
if ($row !== false) {
if (($data = @unserialize($row['data'])) === false) {
if (!isset($row['data']) || ($data = @unserialize($row['data'])) === false) {
$data = null;
}
return new Assignment([
......@@ -349,7 +349,7 @@ class DbManager extends Manager
->queryAll();
$assignments = [];
foreach ($rows as $row) {
if (($data = @unserialize($row['data'])) === false) {
if (!isset($row['data']) || ($data = @unserialize($row['data'])) === false) {
$data = null;
}
$assignments[$row['item_name']] = new Assignment([
......@@ -372,7 +372,7 @@ class DbManager extends Manager
$this->db->createCommand()
->update($this->assignmentTable, [
'biz_rule' => $assignment->bizRule,
'data' => serialize($assignment->data),
'data' => $assignment->data === null ? null : serialize($assignment->data),
], [
'user_id' => $assignment->userId,
'item_name' => $assignment->itemName,
......@@ -411,7 +411,7 @@ class DbManager extends Manager
}
$items = [];
foreach ($command->queryAll() as $row) {
if (($data = @unserialize($row['data'])) === false) {
if (!isset($row['data']) || ($data = @unserialize($row['data'])) === false) {
$data = null;
}
$items[$row['name']] = new Item([
......@@ -449,7 +449,7 @@ class DbManager extends Manager
'type' => $type,
'description' => $description,
'biz_rule' => $bizRule,
'data' => serialize($data),
'data' => $data === null ? null : serialize($data),
])
->execute();
return new Item([
......@@ -496,7 +496,7 @@ class DbManager extends Manager
->queryOne();
if ($row !== false) {
if (($data = @unserialize($row['data'])) === false) {
if (!isset($row['data']) || ($data = @unserialize($row['data'])) === false) {
$data = null;
}
return new Item([
......@@ -537,7 +537,7 @@ class DbManager extends Manager
'type' => $item->type,
'description' => $item->description,
'biz_rule' => $item->bizRule,
'data' => serialize($item->data),
'data' => $item->data === null ? null : serialize($item->data),
], [
'name' => $oldName === null ? $item->getName() : $oldName,
])
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\test;
use Yii;
use yii\base\ArrayAccessTrait;
use yii\base\InvalidConfigException;
use yii\db\TableSchema;
/**
* ActiveFixture represents a fixture backed up by a [[modelClass|ActiveRecord class]] or a [[tableName|database table]].
*
* Either [[modelClass]] or [[tableName]] must be set. You should also provide fixture data in the file
* specified by [[dataFile]] or overriding [[getData()]] if you want to use code to generate the fixture data.
*
* When the fixture is being loaded, it will first call [[resetTable()]] to remove any existing data in the table.
* It will then populate the table with the data returned by [[getData()]].
*
* After the fixture is loaded, you can access the loaded data via the [[data]] property. If you set [[modelClass]],
* you will also be able to retrieve an instance of [[modelClass]] with the populated data via [[getModel()]].
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class ActiveFixture extends BaseActiveFixture
{
/**
* @var string the name of the database table that this fixture is about. If this property is not set,
* the table name will be determined via [[modelClass]].
* @see modelClass
*/
public $tableName;
/**
* @var string|boolean the file path or path alias of the data file that contains the fixture data
* to be returned by [[getData()]]. If this is not set, it will default to `FixturePath/data/TableName.php`,
* where `FixturePath` stands for the directory containing this fixture class, and `TableName` stands for the
* name of the table associated with this fixture. You can set this property to be false to prevent loading any data.
*/
public $dataFile;
/**
* @var TableSchema the table schema for the table associated with this fixture
*/
private $_table;
/**
* @inheritdoc
*/
public function init()
{
parent::init();
if (!isset($this->modelClass) && !isset($this->tableName)) {
throw new InvalidConfigException('Either "modelClass" or "tableName" must be set.');
}
}
/**
* Loads the fixture.
*
* The default implementation will first clean up the table by calling [[resetTable()]].
* It will then populate the table with the data returned by [[getData()]].
*
* If you override this method, you should consider calling the parent implementation
* so that the data returned by [[getData()]] can be populated into the table.
*/
public function load()
{
$this->resetTable();
$table = $this->getTableSchema();
foreach ($this->getData() as $alias => $row) {
$this->db->createCommand()->insert($table->fullName, $row)->execute();
if ($table->sequenceName !== null) {
foreach ($table->primaryKey as $pk) {
if (!isset($row[$pk])) {
$row[$pk] = $this->db->getLastInsertID($table->sequenceName);
break;
}
}
}
$this->data[$alias] = $row;
}
}
/**
* Returns the fixture data.
*
* The default implementation will try to return the fixture data by including the external file specified by [[dataFile]].
* The file should return an array of data rows (column name => column value), each corresponding to a row in the table.
*
* If the data file does not exist, an empty array will be returned.
*
* @return array the data rows to be inserted into the database table.
*/
protected function getData()
{
if ($this->dataFile === null) {
$class = new \ReflectionClass($this);
$dataFile = dirname($class->getFileName()) . '/data/' . $this->getTableSchema()->fullName . '.php';
return is_file($dataFile) ? require($dataFile) : [];
} else {
return parent::getData();
}
}
/**
* Removes all existing data from the specified table and resets sequence number to 1 (if any).
* This method is called before populating fixture data into the table associated with this fixture.
*/
protected function resetTable()
{
$table = $this->getTableSchema();
$this->db->createCommand()->delete($table->fullName)->execute();
if ($table->sequenceName !== null) {
$this->db->createCommand()->resetSequence($table->fullName, 1)->execute();
}
}
/**
* @return TableSchema the schema information of the database table associated with this fixture.
* @throws \yii\base\InvalidConfigException if the table does not exist
*/
public function getTableSchema()
{
if ($this->_table !== null) {
return $this->_table;
}
$db = $this->db;
$tableName = $this->tableName;
if ($tableName === null) {
/** @var \yii\db\ActiveRecord $modelClass */
$modelClass = $this->modelClass;
$tableName = $modelClass::tableName();
}
$this->_table = $db->getSchema()->getTableSchema($tableName);
if ($this->_table === null) {
throw new InvalidConfigException("Table does not exist: {$tableName}");
}
return $this->_table;
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\test;
use Yii;
use yii\base\ArrayAccessTrait;
use yii\base\InvalidConfigException;
/**
* BaseActiveFixture is the base class for fixture classes that support accessing fixture data as ActiveRecord objects.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
abstract class BaseActiveFixture extends DbFixture implements \IteratorAggregate, \ArrayAccess, \Countable
{
use ArrayAccessTrait;
/**
* @var string the AR model class associated with this fixture.
* @see tableName
*/
public $modelClass;
/**
* @var array the data rows. Each array element represents one row of data (column name => column value).
*/
public $data = [];
/**
* @var string|boolean the file path or path alias of the data file that contains the fixture data
* to be returned by [[getData()]]. You can set this property to be false to prevent loading any data.
*/
public $dataFile;
/**
* @var \yii\db\ActiveRecord[] the loaded AR models
*/
private $_models = [];
/**
* Returns the AR model by the specified model name.
* A model name is the key of the corresponding data row in [[data]].
* @param string $name the model name.
* @return null|\yii\db\ActiveRecord the AR model, or null if the model cannot be found in the database
* @throws \yii\base\InvalidConfigException if [[modelClass]] is not set.
*/
public function getModel($name)
{
if (!isset($this->data[$name])) {
return null;
}
if (array_key_exists($name, $this->_models)) {
return $this->_models[$name];
}
if ($this->modelClass === null) {
throw new InvalidConfigException('The "modelClass" property must be set.');
}
$row = $this->data[$name];
/** @var \yii\db\ActiveRecord $modelClass */
$modelClass = $this->modelClass;
/** @var \yii\db\ActiveRecord $model */
$model = new $modelClass;
$keys = [];
foreach ($model->primaryKey() as $key) {
$keys[$key] = isset($row[$key]) ? $row[$key] : null;
}
return $this->_models[$name] = $modelClass::find($keys);
}
/**
* Loads the fixture.
*
* The default implementation simply stores the data returned by [[getData()]] in [[data]].
* You should usually override this method by putting the data into the underlying database.
*/
public function load()
{
$this->data = $this->getData();
}
/**
* Returns the fixture data.
*
* The default implementation will try to return the fixture data by including the external file specified by [[dataFile]].
* The file should return the data array that will be stored in [[data]] after inserting into the database.
*
* @return array the data to be put into the database
* @throws InvalidConfigException if the specified data file does not exist.
*/
protected function getData()
{
if ($this->dataFile === false || $this->dataFile === null) {
return [];
}
$dataFile = Yii::getAlias($this->dataFile);
if (is_file($dataFile)) {
return require($dataFile);
} else {
throw new InvalidConfigException("Fixture data file does not exist: {$this->dataFile}");
}
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\test;
use Yii;
use yii\base\InvalidConfigException;
use yii\db\Connection;
/**
* DbFixture is the base class for DB-related fixtures.
*
* DbFixture provides the [[db]] connection to be used by DB fixtures.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
abstract class DbFixture extends Fixture
{
/**
* @var Connection|string the DB connection object or the application component ID of the DB connection.
* After the DbFixture object is created, if you want to change this property, you should only assign it
* with a DB connection object.
*/
public $db = 'db';
/**
* @inheritdoc
*/
public function init()
{
parent::init();
if (is_string($this->db)) {
$this->db = Yii::$app->getComponent($this->db);
}
if (!is_object($this->db)) {
throw new InvalidConfigException("The 'db' property must be either a DB connection instance or the application component ID of a DB connection.");
}
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\test;
use Yii;
use yii\base\Component;
use yii\base\InvalidConfigException;
use yii\db\ActiveRecord;
use yii\db\Connection;
/**
* DbFixtureManager manages database fixtures during tests.
*
* A fixture represents a list of rows for a specific table. For a test method,
* using a fixture means that at the beginning of the method, the table has and only
* has the rows that are given in the fixture. Therefore, the table's state is
* predictable.
*
* A fixture is represented as a PHP script whose name (without suffix) is the
* same as the table name (if schema name is needed, it should be prefixed to
* the table name). The PHP script returns an array representing a list of table
* rows. Each row is an associative array of column values indexed by column names.
*
* Fixtures must be stored under the [[basePath]] directory. The directory
* may contain a file named `init.php` which will be executed before any fixture is loaded.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class DbFixtureManager extends Component
{
/**
* @var string the init script file that should be executed before running each test.
* This should be a path relative to [[basePath]].
*/
public $initScript = 'init.php';
/**
* @var string the base path containing all fixtures. This can be either a directory path or path alias.
*/
public $basePath = '@app/tests/fixtures';
/**
* @var Connection|string the DB connection object or the application component ID of the DB connection.
* After the DbFixtureManager object is created, if you want to change this property, you should only assign it
* with a DB connection object.
*/
public $db = 'db';
/**
* @var array list of database schemas that the test tables may reside in. Defaults to
* [''], meaning using the default schema (an empty string refers to the
* default schema). This property is mainly used when turning on and off integrity checks
* so that fixture data can be populated into the database without causing problem.
*/
public $schemas = [''];
private $_rows; // fixture name, row alias => row
private $_models; // fixture name, row alias => record (or class name)
/**
* Loads the specified fixtures.
*
* This method does the following things to load the fixtures:
*
* - Run [[initScript]] if any.
* - Clean up data and models loaded in memory previously.
* - Load each specified fixture by calling [[loadFixture()]].
*
* @param array $fixtures a list of fixtures (fixture name => table name or AR class name) to be loaded.
* Each array element can be either a table name (with schema prefix if needed), or a fully-qualified
* ActiveRecord class name (e.g. `app\models\Post`). An element can be associated with a key
* which will be treated as the fixture name.
* @return array the loaded fixture data (fixture name => table rows)
* @throws InvalidConfigException if a model class specifying a fixture is not an ActiveRecord class.
*/
public function load(array $fixtures = [])
{
$this->basePath = Yii::getAlias($this->basePath);
if (is_string($this->db)) {
$this->db = Yii::$app->getComponent($this->db);
}
if (!$this->db instanceof Connection) {
throw new InvalidConfigException("The 'db' property must be either a DB connection instance or the application component ID of a DB connection.");
}
foreach ($fixtures as $name => $fixture) {
if (strpos($fixture, '\\') !== false) {
$model = new $fixture;
if ($model instanceof ActiveRecord) {
$fixtures[$name] = $model->getTableSchema()->name;
} else {
throw new InvalidConfigException("Fixture '$fixture' must be an ActiveRecord class.");
}
}
}
$this->_rows = $this->_models = [];
$this->checkIntegrity(false);
if (!empty($this->initScript)) {
$initFile = $this->basePath . '/' . $this->initScript;
if (is_file($initFile)) {
require($initFile);
}
}
foreach ($fixtures as $name => $tableName) {
$rows = $this->loadFixture($tableName);
if (is_array($rows)) {
$this->_rows[$name] = $rows;
}
}
$this->checkIntegrity(true);
return $this->_rows;
}
/**
* Loads the fixture for the specified table.
*
* This method does the following tasks to load the fixture for a table:
*
* - Remove existing rows in the table.
* - If there is any auto-incremental column, the corresponding sequence will be reset to 0.
* - If a fixture file is found, it will be executed, and its return value will be treated
* as rows which will then be inserted into the table.
*
* @param string $tableName table name
* @return array|boolean the loaded fixture rows indexed by row aliases (if any).
* False is returned if the table does not have a fixture.
* @throws InvalidConfigException if the specified table does not exist
*/
public function loadFixture($tableName)
{
$table = $this->db->getSchema()->getTableSchema($tableName);
if ($table === null) {
throw new InvalidConfigException("Table does not exist: $tableName");
}
$this->db->createCommand()->delete($tableName)->execute();
$this->db->createCommand()->resetSequence($tableName, 1)->execute();
$fileName = $this->basePath . '/' . $tableName . '.php';
if (!is_file($fileName)) {
return false;
}
$rows = [];
foreach (require($fileName) as $alias => $row) {
$this->db->createCommand()->insert($tableName, $row)->execute();
if ($table->sequenceName !== null) {
foreach ($table->primaryKey as $pk) {
if (!isset($row[$pk])) {
$row[$pk] = $this->db->getLastInsertID($table->sequenceName);
break;
}
}
}
$rows[$alias] = $row;
}
return $rows;
}
/**
* Returns the fixture data rows.
* The rows will have updated primary key values if the primary key is auto-incremental.
* @param string $fixtureName the fixture name
* @return array the fixture data rows. False is returned if there is no such fixture data.
*/
public function getRows($fixtureName)
{
return isset($this->_rows[$fixtureName]) ? $this->_rows[$fixtureName] : false;
}
/**
* Returns the specified ActiveRecord instance in the fixture data.
* @param string $fixtureName the fixture name
* @param string $modelName the alias for the fixture data row
* @return ActiveRecord the ActiveRecord instance. Null is returned if there is no such fixture row.
*/
public function getModel($fixtureName, $modelName)
{
if (!isset($this->_rows[$fixtureName][$modelName])) {
return null;
}
if (isset($this->_models[$fixtureName][$modelName])) {
return $this->_models[$fixtureName][$modelName];
}
$row = $this->_rows[$fixtureName][$modelName];
/** @var ActiveRecord $modelClass */
$modelClass = $this->_models[$fixtureName];
/** @var ActiveRecord $model */
$model = new $modelClass;
$keys = [];
foreach ($model->primaryKey() as $key) {
$keys[$key] = isset($row[$key]) ? $row[$key] : null;
}
return $this->_models[$fixtureName][$modelName] = $modelClass::find($keys);
}
/**
* Enables or disables database integrity check.
* This method may be used to temporarily turn off foreign constraints check.
* @param boolean $check whether to enable database integrity check
*/
public function checkIntegrity($check)
{
foreach ($this->schemas as $schema) {
$this->db->createCommand()->checkIntegrity($check, $schema)->execute();
}
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\test;
use Yii;
/**
* DbTestTrait implements the commonly used methods for setting up and accessing fixture data.
*
* To use DbTestTrait, call the [[loadFixtures()]] method in the setup method in a test case class.
* The specified fixtures will be loaded and accessible through [[getFixtureData()]] and [[getFixtureModel()]].
*
* For example,
*
* ~~~
* use yii\test\DbTestTrait;
* use yii\codeception\TestCase;
* use app\models\Post;
* use app\models\User;
*
* class PostTestCase extends TestCase
* {
* use DbTestTrait;
*
* protected function setUp()
* {
* parent::setUp();
*
* $this->loadFixtures([
* 'posts' => Post::className(),
* 'users' => User::className(),
* ]);
* }
* }
* ~~~
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
trait DbTestTrait
{
/**
* Loads the specified fixtures.
*
* This method should typically be called in the setup method of test cases so that
* the fixtures are loaded before running each test method.
*
* This method does the following things:
*
* - Run [[DbFixtureManager::initScript]] if it is found under [[DbFixtureManager::basePath]].
* - Clean up data and models loaded in memory previously.
* - Load each specified fixture:
* * Truncate the corresponding table.
* * If a fixture file named `TableName.php` is found under [[DbFixtureManager::basePath]],
* the file will be executed, and the return value will be treated as rows which will
* then be inserted into the table.
*
* @param array $fixtures a list of fixtures (fixture name => table name or AR class name) to be loaded.
* Each array element can be either a table name (with schema prefix if needed), or a fully-qualified
* ActiveRecord class name (e.g. `app\models\Post`). An element can be optionally associated with a key
* which will be treated as the fixture name. For example,
*
* ~~~
* [
* 'tbl_comment',
* 'users' => 'tbl_user', // 'users' is the fixture name, 'tbl_user' is a table name
* 'posts' => 'app\models\Post, // 'app\models\Post' is a model class name
* ]
* ~~~
*
* @return array the loaded fixture data (fixture name => table rows)
*/
public function loadFixtures(array $fixtures = [])
{
return $this->getFixtureManager()->load($fixtures);
}
/**
* Returns the DB fixture manager.
* @return DbFixtureManager the DB fixture manager
*/
public function getFixtureManager()
{
return Yii::$app->getComponent('fixture');
}
/**
* Returns the table rows of the named fixture.
* @param string $fixtureName the fixture name.
* @return array the named fixture table rows. False is returned if there is no such fixture data.
*/
public function getFixtureRows($fixtureName)
{
return $this->getFixtureManager()->getRows($fixtureName);
}
/**
* Returns the named AR instance corresponding to the named fixture.
* @param string $fixtureName the fixture name.
* @param string $modelName the name of the fixture data row
* @return \yii\db\ActiveRecord the named AR instance corresponding to the named fixture.
* Null is returned if there is no such fixture or the record cannot be found.
*/
public function getFixtureModel($fixtureName, $modelName)
{
return $this->getFixtureManager()->getModel($fixtureName, $modelName);
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\test;
use yii\base\Component;
/**
* Fixture represents a fixed state of a test environment.
*
* Each fixture instance represents a particular aspect of a test environment. For example,
* you may use `UserFixture` to initialize the user database table with a set of known data. You may
* load the fixture when running every test method so that the user table always contains the fixed data
* and thus allows your test predictable and repeatable.
*
* A fixture may depend on other fixtures, specified via the [[depends]] property. When a fixture is being loaded,
* its dependent fixtures will be automatically loaded BEFORE the fixture; and when the fixture is being unloaded,
* its dependent fixtures will be unloaded AFTER the fixture.
*
* You should normally override [[load()]] to specify how to set up a fixture; and override [[unload()]]
* for clearing up a fixture.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Fixture extends Component
{
/**
* @var array the fixtures that this fixture depends on. This must be a list of the dependent
* fixture class names.
*/
public $depends = [];
/**
* Loads the fixture.
* This method is called before performing every test method.
* You should override this method with concrete implementation about how to set up the fixture.
*/
public function load()
{
}
/**
* This method is called BEFORE any fixture data is loaded for the current test.
*/
public function beforeLoad()
{
}
/**
* This method is called AFTER all fixture data have been loaded for the current test.
*/
public function afterLoad()
{
}
/**
* Unloads the fixture.
* This method is called after every test method finishes.
* You may override this method to perform necessary cleanup work for the fixture.
*/
public function unload()
{
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\test;
use Yii;
use yii\base\InvalidConfigException;
use yii\base\UnknownMethodException;
use yii\base\UnknownPropertyException;
/**
* FixtureTrait provides functionalities for loading, unloading and accessing fixtures for a test case.
*
* By using FixtureTrait, a test class will be able to specify which fixtures to load by overriding
* the [[fixtures()]] method. It can then load and unload the fixtures using [[loadFixtures()]] and [[unloadFixtures()]].
* Once a fixture is loaded, it can be accessed like an object property, thanks to the PHP `__get()` magic method.
* Also, if the fixture is an instance of [[ActiveFixture]], you will be able to access AR models
* through the syntax `$this->fixtureName('model name')`.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
trait FixtureTrait
{
/**
* @var array the list of fixture objects available for the current test.
* The array keys are the corresponding fixture class names.
* The fixtures are listed in their dependency order. That is, fixture A is listed before B
* if B depends on A.
*/
private $_fixtures;
/**
* @var array the fixture class names indexed by the corresponding fixture names (aliases).
*/
private $_fixtureAliases;
/**
* Returns the value of an object property.
*
* Do not call this method directly as it is a PHP magic method that
* will be implicitly called when executing `$value = $object->property;`.
* @param string $name the property name
* @return mixed the property value
* @throws UnknownPropertyException if the property is not defined
*/
public function __get($name)
{
$fixture = $this->getFixture($name);
if ($fixture !== null) {
return $fixture;
} else {
throw new UnknownPropertyException('Getting unknown property: ' . get_class($this) . '::' . $name);
}
}
/**
* Calls the named method which is not a class method.
*
* Do not call this method directly as it is a PHP magic method that
* will be implicitly called when an unknown method is being invoked.
* @param string $name the method name
* @param array $params method parameters
* @throws UnknownMethodException when calling unknown method
* @return mixed the method return value
*/
public function __call($name, $params)
{
$fixture = $this->getFixture($name);
if ($fixture instanceof ActiveFixture) {
return $fixture->getModel(reset($params));
} else {
throw new UnknownMethodException('Unknown method: ' . get_class($this) . "::$name()");
}
}
/**
* Declares the fixtures that are needed by the current test case.
* The return value of this method must be an array of fixture configurations. For example,
*
* ```php
* [
* // anonymous fixture
* PostFixture::className(),
* // "users" fixture
* 'users' => UserFixture::className(),
* // "cache" fixture with configuration
* 'cache' => [
* 'class' => CacheFixture::className(),
* 'host' => 'xxx',
* ],
* ]
* ```
*
* Note that the actual fixtures used for a test case will include both [[globalFixtures()]]
* and [[fixtures()]].
*
* @return array the fixtures needed by the current test case
*/
protected function fixtures()
{
return [];
}
/**
* Declares the fixtures shared required by different test cases.
* The return value should be similar to that of [[fixtures()]].
* You should usually override this method in a base class.
* @return array the fixtures shared and required by different test cases.
* @see fixtures()
*/
protected function globalFixtures()
{
return [];
}
/**
* Loads the fixtures.
* This method will load the fixtures specified by `$fixtures` or [[globalFixtures()]] and [[fixtures()]].
* @param array $fixtures the fixtures to loaded. If not set, [[fixtures()]] will be loaded instead.
* @throws InvalidConfigException if fixtures are not properly configured or if a circular dependency among
* the fixtures is detected.
*/
protected function loadFixtures($fixtures = null)
{
if ($fixtures === null) {
$fixtures = array_merge($this->globalFixtures(), $this->fixtures());
}
// normalize fixture configurations
$config = []; // configuration provided in test case
$this->_fixtureAliases = [];
foreach ($fixtures as $name => $fixture) {
if (!is_array($fixture)) {
$fixtures[$name] = $fixture = ['class' => $fixture];
} elseif (!isset($fixture['class'])) {
throw new InvalidConfigException("You must specify 'class' for the fixture '$name'.");
}
$config[$fixture['class']] = $fixture;
$this->_fixtureAliases[$name] = $fixture['class'];
}
// create fixture instances
$this->_fixtures = [];
$stack = array_reverse($fixtures);
while (($fixture = array_pop($stack)) !== null) {
if ($fixture instanceof Fixture) {
$class = get_class($fixture);
unset($this->_fixtures[$class]); // unset so that the fixture is added to the last in the next line
$this->_fixtures[$class] = $fixture;
} else {
$class = $fixture['class'];
if (!isset($this->_fixtures[$class])) {
$this->_fixtures[$class] = false;
$stack[] = $fixture = Yii::createObject($fixture);
foreach ($fixture->depends as $dep) {
// need to use the configuration provided in test case
$stack[] = isset($config[$dep]) ? $config[$dep] : ['class' => $dep];
}
} elseif ($this->_fixtures[$class] === false) {
throw new InvalidConfigException("A circular dependency is detected for fixture '$class'.");
}
}
}
// load fixtures
/** @var Fixture $fixture */
foreach ($this->_fixtures as $fixture) {
$fixture->beforeLoad();
}
foreach ($this->_fixtures as $fixture) {
$fixture->load();
}
foreach ($this->_fixtures as $fixture) {
$fixture->afterLoad();
}
}
/**
* Unloads all existing fixtures.
*/
protected function unloadFixtures()
{
/** @var Fixture $fixture */
foreach (array_reverse($this->_fixtures) as $fixture) {
$fixture->unload();
}
}
/**
* @return array the loaded fixtures for the current test case
*/
protected function getFixtures()
{
return $this->_fixtures;
}
/**
* Returns the named fixture.
* @param string $name the fixture alias or class name
* @return Fixture the fixture object, or null if the named fixture does not exist.
*/
protected function getFixture($name)
{
$class = isset($this->_fixtureAliases[$name]) ? $this->_fixtureAliases[$name] : $name;
return isset($this->_fixtures[$class]) ? $this->_fixtures[$class] : null;
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\test;
use Yii;
/**
* InitDbFixture represents the initial state needed for DB-related tests.
*
* Its main task is to toggle integrity check of the database during data loading.
* This is needed by other DB-related fixtures (e.g. [[ActiveFixture]]) so that they can populate
* data into the database without triggering integrity check errors.
*
* Besides, DbFixture also attempts to load an [[initScript|initialization script]] if it exists.
*
* You should normally use InitDbFixture to prepare a skeleton test database.
* Other DB fixtures will then add specific tables and data to this database.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class InitDbFixture extends DbFixture
{
/**
* @var string the init script file that should be executed when loading this fixture.
* This should be either a file path or path alias. Note that if the file does not exist,
* no error will be raised.
*/
public $initScript = '@app/tests/fixtures/initdb.php';
/**
* @var array list of database schemas that the test tables may reside in. Defaults to
* `['']`, meaning using the default schema (an empty string refers to the
* default schema). This property is mainly used when turning on and off integrity checks
* so that fixture data can be populated into the database without causing problem.
*/
public $schemas = [''];
/**
* @inheritdoc
*/
public function beforeLoad()
{
foreach ($this->schemas as $schema) {
$this->checkIntegrity(false, $schema);
}
}
/**
* @inheritdoc
*/
public function afterLoad()
{
foreach ($this->schemas as $schema) {
$this->checkIntegrity(true, $schema);
}
}
/**
* @inheritdoc
*/
public function load()
{
$file = Yii::getAlias($this->initScript);
if (is_file($file)) {
require($file);
}
}
/**
* Toggles the DB integrity check.
* @param boolean $check whether to turn on or off the integrity check.
*/
public function checkIntegrity($check)
{
foreach ($this->schemas as $schema) {
$this->db->createCommand()->checkIntegrity($check, $schema)->execute();
}
}
}
......@@ -108,9 +108,9 @@ class AccessControl extends ActionFilter
return true;
} elseif ($allow === false) {
if (isset($rule->denyCallback)) {
call_user_func($rule->denyCallback, $rule);
call_user_func($rule->denyCallback, $rule, $action);
} elseif (isset($this->denyCallback)) {
call_user_func($this->denyCallback, $rule);
call_user_func($this->denyCallback, $rule, $action);
} else {
$this->denyAccess($user);
}
......@@ -118,9 +118,8 @@ class AccessControl extends ActionFilter
}
}
if (isset($this->denyCallback)) {
call_user_func($this->denyCallback, $rule);
}
else {
call_user_func($this->denyCallback, $rule, $action);
} else {
$this->denyAccess($user);
}
return false;
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\web;
use yii\base\InvalidParamException;
use yii\helpers\Json;
/**
* Parses a raw HTTP request using [[yii\helpers\Json::decode()]]
*
* @author Dan Schmidt <danschmidt5189@gmail.com>
* @since 2.0
*/
class JsonParser implements RequestParserInterface
{
/**
* @var boolean whether to return objects in terms of associative arrays.
*/
public $asArray = true;
/**
* @var boolean whether to throw a [[BadRequestHttpException]] if the body is invalid json
*/
public $throwException = true;
/**
* Parses a HTTP request body.
* @param string $rawBody the raw HTTP request body.
* @param string $contentType the content type specified for the request body.
* @return array parameters parsed from the request body
* @throws BadRequestHttpException if the body contains invalid json and [[throwException]] is `true`.
*/
public function parse($rawBody, $contentType)
{
try {
return Json::decode($rawBody, $this->asArray);
} catch (InvalidParamException $e) {
if ($this->throwException) {
throw new BadRequestHttpException('Invalid JSON data in request body: ' . $e->getMessage(), 0, $e);
}
return null;
}
}
}
......@@ -9,6 +9,8 @@ namespace yii\web;
use Yii;
use yii\base\InvalidConfigException;
use yii\base\InvalidParamException;
use yii\helpers\Json;
use yii\helpers\Security;
use yii\helpers\StringHelper;
......@@ -23,10 +25,9 @@ use yii\helpers\StringHelper;
* You can access that instance via `Yii::$app->request`.
*
* @property string $absoluteUrl The currently requested absolute URL. This property is read-only.
* @property string $acceptTypes User browser accept types, null if not present. This property is read-only.
* @property array $acceptedContentTypes The content types ordered by the preference level. The first element
* @property array $acceptableContentTypes The content types ordered by the preference level. The first element
* represents the most preferred content type.
* @property array $acceptedLanguages The languages ordered by the preference level. The first element
* @property array $acceptableLanguages The languages ordered by the preference level. The first element
* represents the most preferred language.
* @property string $baseUrl The relative URL for the application.
* @property string $cookieValidationKey The secret key used for cookie validation. If it was not set
......@@ -58,8 +59,7 @@ use yii\helpers\StringHelper;
* mark. Note, the returned path info is already URL-decoded.
* @property integer $port Port number for insecure requests.
* @property array $post The POST request parameter values. This property is read-only.
* @property string $preferredLanguage The language that the application should use. Null is returned if both
* [[getAcceptedLanguages()]] and `$languages` are empty. This property is read-only.
* @property string $preferredLanguage The language that the application should use. This property is read-only.
* @property array $put The PUT request parameter values. This property is read-only.
* @property string $queryString Part of the request URL that is after the question mark. This property is
* read-only.
......@@ -128,6 +128,26 @@ class Request extends \yii\base\Request
* @see getRestParams()
*/
public $restVar = '_method';
/**
* @var array the parsers for converting the raw HTTP request body into [[restParams]].
* The array keys are the request `Content-Types`, and the array values are the
* corresponding configurations for [[Yii::createObject|creating the parser objects]].
* A parser must implement the [[RequestParserInterface]].
*
* To enable parsing for JSON requests you can use the [[JsonParser]] class like in the following example:
*
* ```
* [
* 'application/json' => 'yii\web\JsonParser',
* ]
* ```
*
* To register a parser for parsing all request types you can use `'*'` as the array key.
* This one will be used as a fallback in case no other types match.
*
* @see getRestParams()
*/
public $parsers = [];
private $_cookies;
......@@ -249,14 +269,33 @@ class Request extends \yii\base\Request
/**
* Returns the request parameters for the RESTful request.
*
* Request parameters are determined using the parsers configured in [[parsers]] property.
* If no parsers are configured for the current [[contentType]] it uses the PHP function [[mb_parse_str()]]
* to parse the [[rawBody|request body]].
* @return array the RESTful request parameters
* @throws \yii\base\InvalidConfigException if a registered parser does not implement the [[RequestParserInterface]].
* @see getMethod()
*/
public function getRestParams()
{
if ($this->_restParams === null) {
$contentType = $this->getContentType();
if (isset($_POST[$this->restVar])) {
$this->_restParams = $_POST;
unset($this->_restParams[$this->restVar]);
} elseif (isset($this->parsers[$contentType])) {
$parser = Yii::createObject($this->parsers[$contentType]);
if (!($parser instanceof RequestParserInterface)) {
throw new InvalidConfigException("The '$contentType' request parser is invalid. It must implement the yii\\web\\RequestParserInterface.");
}
$this->_restParams = $parser->parse($this->getRawBody(), $contentType);
} elseif (isset($this->parsers['*'])) {
$parser = Yii::createObject($this->parsers['*']);
if (!($parser instanceof RequestParserInterface)) {
throw new InvalidConfigException("The fallback request parser is invalid. It must implement the yii\\web\\RequestParserInterface.");
}
$this->_restParams = $parser->parse($this->getRawBody(), $contentType);
} else {
$this->_restParams = [];
mb_parse_str($this->getRawBody(), $this->_restParams);
......@@ -730,15 +769,6 @@ class Request extends \yii\base\Request
return isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : null;
}
/**
* Returns user browser accept types, null if not present.
* @return string user browser accept types, null if not present
*/
public function getAcceptTypes()
{
return isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : null;
}
private $_port;
/**
......@@ -804,12 +834,12 @@ class Request extends \yii\base\Request
private $_contentTypes;
/**
* Returns the content types accepted by the end user.
* Returns the content types acceptable by the end user.
* This is determined by the `Accept` HTTP header.
* @return array the content types ordered by the preference level. The first element
* represents the most preferred content type.
*/
public function getAcceptedContentTypes()
public function getAcceptableContentTypes()
{
if ($this->_contentTypes === null) {
if (isset($_SERVER['HTTP_ACCEPT'])) {
......@@ -822,23 +852,38 @@ class Request extends \yii\base\Request
}
/**
* @param array $value the content types that are accepted by the end user. They should
* @param array $value the content types that are acceptable by the end user. They should
* be ordered by the preference level.
*/
public function setAcceptedContentTypes($value)
public function setAcceptableContentTypes($value)
{
$this->_contentTypes = $value;
}
/**
* Returns request content-type
* The Content-Type header field indicates the MIME type of the data
* contained in [[getRawBody()]] or, in the case of the HEAD method, the
* media type that would have been sent had the request been a GET.
* For the MIME-types the user expects in response, see [[acceptableContentTypes]].
* @return string request content-type. Null is returned if this information is not available.
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17
* HTTP 1.1 header field definitions
*/
public function getContentType()
{
return isset($_SERVER["CONTENT_TYPE"]) ? $_SERVER["CONTENT_TYPE"] : null;
}
private $_languages;
/**
* Returns the languages accepted by the end user.
* Returns the languages acceptable by the end user.
* This is determined by the `Accept-Language` HTTP header.
* @return array the languages ordered by the preference level. The first element
* represents the most preferred language.
*/
public function getAcceptedLanguages()
public function getAcceptableLanguages()
{
if ($this->_languages === null) {
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
......@@ -851,17 +896,17 @@ class Request extends \yii\base\Request
}
/**
* @param array $value the languages that are accepted by the end user. They should
* @param array $value the languages that are acceptable by the end user. They should
* be ordered by the preference level.
*/
public function setAcceptedLanguages($value)
public function setAcceptableLanguages($value)
{
$this->_languages = $value;
}
/**
* Parses the given `Accept` (or `Accept-Language`) header.
* This method will return the accepted values ordered by their preference level.
* This method will return the acceptable values ordered by their preference level.
* @param string $header the header to be parsed
* @return array the accept values ordered by their preference level.
*/
......@@ -906,23 +951,21 @@ class Request extends \yii\base\Request
* Returns the user-preferred language that should be used by this application.
* The language resolution is based on the user preferred languages and the languages
* supported by the application. The method will try to find the best match.
* @param array $languages a list of the languages supported by the application.
* If empty, this method will return the first language returned by [[getAcceptedLanguages()]].
* @return string the language that the application should use. Null is returned if both [[getAcceptedLanguages()]]
* and `$languages` are empty.
* @param array $languages a list of the languages supported by the application. If this is empty, the current
* application language will be returned without further processing.
* @return string the language that the application should use.
*/
public function getPreferredLanguage($languages = [])
public function getPreferredLanguage(array $languages = [])
{
$acceptedLanguages = $this->getAcceptedLanguages();
if (empty($languages)) {
return isset($acceptedLanguages[0]) ? $acceptedLanguages[0] : null;
return Yii::$app->language;
}
foreach ($acceptedLanguages as $acceptedLanguage) {
$acceptedLanguage = str_replace('_', '-', strtolower($acceptedLanguage));
foreach ($this->getAcceptableLanguages() as $acceptableLanguage) {
$acceptableLanguage = str_replace('_', '-', strtolower($acceptableLanguage));
foreach ($languages as $language) {
$language = str_replace('_', '-', strtolower($language));
// en-us==en-us, en==en-us, en-us==en
if ($language === $acceptedLanguage || strpos($acceptedLanguage, $language . '-') === 0 || strpos($language, $acceptedLanguage . '-') === 0) {
if ($language === $acceptableLanguage || strpos($acceptableLanguage, $language . '-') === 0 || strpos($language, $acceptableLanguage . '-') === 0) {
return $language;
}
}
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\web;
/**
* Interface for classes that parse the raw request body into a parameters array.
*
* @author Dan Schmidt <danschmidt5189@gmail.com>
* @since 2.0
*/
interface RequestParserInterface
{
/**
* Parses a HTTP request body.
* @param string $rawBody the raw HTTP request body.
* @param string $contentType the content type specified for the request body.
* @return array parameters parsed from the request body
*/
public function parse($rawBody, $contentType);
}
......@@ -39,9 +39,9 @@ class MaskedInput extends InputWidget
/**
* @var string the input mask (e.g. '99/99/9999' for date input). The following characters are predefined:
*
* - `a`: represents an alpha character (A-Z,a-z)
* - `a`: represents an alpha character (A-Z, a-z)
* - `9`: represents a numeric character (0-9)
* - `*`: represents an alphanumeric character (A-Z,a-z,0-9)
* - `*`: represents an alphanumeric character (A-Z, a-z, 0-9)
* - `?`: anything listed after '?' within the mask is considered optional user input
*
* Additional characters can be defined by specifying the [[charMap]] property.
......
......@@ -10,8 +10,9 @@
defined('YII_DEBUG') or define('YII_DEBUG', true);
// fcgi doesn't have STDIN defined by default
// fcgi doesn't have STDIN and STDOUT defined by default
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
defined('STDIN') or define('STDOUT', fopen('php://stdout', 'w'));
require(__DIR__ . '/Yii.php');
......
......@@ -7,7 +7,7 @@ namespace yiiunit\data\ar;
*
* @property integer $id
* @property integer $customer_id
* @property integer $create_time
* @property integer $created_at
* @property string $total
*/
class Order extends ActiveRecord
......@@ -68,7 +68,7 @@ class Order extends ActiveRecord
public function beforeSave($insert)
{
if (parent::beforeSave($insert)) {
$this->create_time = time();
$this->created_at = time();
return true;
} else {
return false;
......
......@@ -32,11 +32,9 @@ class Customer extends ActiveRecord
public function getOrders()
{
return $this->hasMany(Order::className(), array('customer_id' => 'id'))->orderBy('create_time');
return $this->hasMany(Order::className(), ['customer_id' => 'id'])->orderBy('created_at');
}
public function afterSave($insert)
{
ActiveRecordTest::$afterSaveInsert = $insert;
......
......@@ -8,7 +8,7 @@ use yii\elasticsearch\Command;
*
* @property integer $id
* @property integer $customer_id
* @property integer $create_time
* @property integer $created_at
* @property string $total
*/
class Order extends ActiveRecord
......@@ -20,7 +20,7 @@ class Order extends ActiveRecord
public function attributes()
{
return ['id', 'customer_id', 'create_time', 'total'];
return ['id', 'customer_id', 'created_at', 'total'];
}
public function getCustomer()
......@@ -65,7 +65,7 @@ class Order extends ActiveRecord
public function beforeSave($insert)
{
if (parent::beforeSave($insert)) {
// $this->create_time = time();
// $this->created_at = time();
return true;
} else {
return false;
......@@ -84,7 +84,7 @@ class Order extends ActiveRecord
"_id" => ["path" => "id", "index" => "not_analyzed", "store" => "yes"],
"properties" => [
"customer_id" => ["type" => "integer"],
// "create_time" => ["type" => "string", "index" => "not_analyzed"],
// "created_at" => ["type" => "string", "index" => "not_analyzed"],
"total" => ["type" => "integer"],
]
]
......
......@@ -6,7 +6,7 @@ class Order extends ActiveRecord
{
public function attributes()
{
return ['id', 'customer_id', 'create_time', 'total'];
return ['id', 'customer_id', 'created_at', 'total'];
}
public function getCustomer()
......@@ -53,10 +53,10 @@ class Order extends ActiveRecord
public function beforeSave($insert)
{
if (parent::beforeSave($insert)) {
$this->create_time = time();
$this->created_at = time();
return true;
} else {
return false;
}
}
}
\ No newline at end of file
}
......@@ -46,7 +46,7 @@ CREATE TABLE `tbl_item` (
CREATE TABLE `tbl_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`customer_id` int(11) NOT NULL,
`create_time` int(11) NOT NULL,
`created_at` int(11) NOT NULL,
`total` decimal(10,0) NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `FK_order_customer_id` FOREIGN KEY (`customer_id`) REFERENCES `tbl_customer` (`id`) ON DELETE CASCADE
......@@ -107,9 +107,9 @@ INSERT INTO tbl_item (name, category_id) VALUES ('Ice Age', 2);
INSERT INTO tbl_item (name, category_id) VALUES ('Toy Story', 2);
INSERT INTO tbl_item (name, category_id) VALUES ('Cars', 2);
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (1, 1325282384, 110.0);
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (2, 1325334482, 33.0);
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (2, 1325502201, 40.0);
INSERT INTO tbl_order (customer_id, created_at, total) VALUES (1, 1325282384, 110.0);
INSERT INTO tbl_order (customer_id, created_at, total) VALUES (2, 1325334482, 33.0);
INSERT INTO tbl_order (customer_id, created_at, total) VALUES (2, 1325502201, 40.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (1, 1, 1, 30.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (1, 2, 2, 40.0);
......
......@@ -37,7 +37,7 @@ CREATE TABLE [dbo].[tbl_item] (
CREATE TABLE [dbo].[tbl_order] (
[id] [int] IDENTITY(1,1) NOT NULL,
[customer_id] [int] NOT NULL,
[create_time] [int] NOT NULL,
[created_at] [int] NOT NULL,
[total] [decimal](10,0) NOT NULL,
CONSTRAINT [PK_order] PRIMARY KEY CLUSTERED (
[id] ASC
......@@ -92,9 +92,9 @@ INSERT INTO [dbo].[tbl_item] ([name], [category_id]) VALUES ('Ice Age', 2);
INSERT INTO [dbo].[tbl_item] ([name], [category_id]) VALUES ('Toy Story', 2);
INSERT INTO [dbo].[tbl_item] ([name], [category_id]) VALUES ('Cars', 2);
INSERT INTO [dbo].[tbl_order] ([customer_id], [create_time], [total]) VALUES (1, 1325282384, 110.0);
INSERT INTO [dbo].[tbl_order] ([customer_id], [create_time], [total]) VALUES (2, 1325334482, 33.0);
INSERT INTO [dbo].[tbl_order] ([customer_id], [create_time], [total]) VALUES (2, 1325502201, 40.0);
INSERT INTO [dbo].[tbl_order] ([customer_id], [created_at], [total]) VALUES (1, 1325282384, 110.0);
INSERT INTO [dbo].[tbl_order] ([customer_id], [created_at], [total]) VALUES (2, 1325334482, 33.0);
INSERT INTO [dbo].[tbl_order] ([customer_id], [created_at], [total]) VALUES (2, 1325502201, 40.0);
INSERT INTO [dbo].[tbl_order_item] ([order_id], [item_id], [quantity], [subtotal]) VALUES (1, 1, 1, 30.0);
INSERT INTO [dbo].[tbl_order_item] ([order_id], [item_id], [quantity], [subtotal]) VALUES (1, 2, 2, 40.0);
......
......@@ -47,7 +47,7 @@ CREATE TABLE `tbl_item` (
CREATE TABLE `tbl_order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`customer_id` int(11) NOT NULL,
`create_time` int(11) NOT NULL,
`created_at` int(11) NOT NULL,
`total` decimal(10,0) NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `FK_order_customer_id` FOREIGN KEY (`customer_id`) REFERENCES `tbl_customer` (`id`) ON DELETE CASCADE
......@@ -109,9 +109,9 @@ INSERT INTO tbl_item (name, category_id) VALUES ('Ice Age', 2);
INSERT INTO tbl_item (name, category_id) VALUES ('Toy Story', 2);
INSERT INTO tbl_item (name, category_id) VALUES ('Cars', 2);
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (1, 1325282384, 110.0);
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (2, 1325334482, 33.0);
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (2, 1325502201, 40.0);
INSERT INTO tbl_order (customer_id, created_at, total) VALUES (1, 1325282384, 110.0);
INSERT INTO tbl_order (customer_id, created_at, total) VALUES (2, 1325334482, 33.0);
INSERT INTO tbl_order (customer_id, created_at, total) VALUES (2, 1325502201, 40.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (1, 1, 1, 30.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (1, 2, 2, 40.0);
......
......@@ -43,7 +43,7 @@ CREATE TABLE tbl_item (
CREATE TABLE tbl_order (
id serial not null primary key,
customer_id integer NOT NULL references tbl_customer(id) on UPDATE CASCADE on DELETE CASCADE,
create_time integer NOT NULL,
created_at integer NOT NULL,
total decimal(10,0) NOT NULL
);
......@@ -92,9 +92,9 @@ INSERT INTO tbl_item (name, category_id) VALUES ('Ice Age', 2);
INSERT INTO tbl_item (name, category_id) VALUES ('Toy Story', 2);
INSERT INTO tbl_item (name, category_id) VALUES ('Cars', 2);
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (1, 1325282384, 110.0);
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (2, 1325334482, 33.0);
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (2, 1325502201, 40.0);
INSERT INTO tbl_order (customer_id, created_at, total) VALUES (1, 1325282384, 110.0);
INSERT INTO tbl_order (customer_id, created_at, total) VALUES (2, 1325334482, 33.0);
INSERT INTO tbl_order (customer_id, created_at, total) VALUES (2, 1325502201, 40.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (1, 1, 1, 30.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (1, 2, 2, 40.0);
......@@ -130,4 +130,4 @@ INSERT INTO tbl_validator_ref (id, a_field, ref) VALUES (2, 'ref_to_2', 2);
INSERT INTO tbl_validator_ref (id, a_field, ref) VALUES (3, 'ref_to_3', 3);
INSERT INTO tbl_validator_ref (id, a_field, ref) VALUES (4, 'ref_to_4', 4);
INSERT INTO tbl_validator_ref (id, a_field, ref) VALUES (5, 'ref_to_4', 4);
INSERT INTO tbl_validator_ref (id, a_field, ref) VALUES (6, 'ref_to_5', 5);
\ No newline at end of file
INSERT INTO tbl_validator_ref (id, a_field, ref) VALUES (6, 'ref_to_5', 5);
......@@ -37,7 +37,7 @@ CREATE TABLE tbl_item (
CREATE TABLE tbl_order (
id INTEGER NOT NULL,
customer_id INTEGER NOT NULL,
create_time INTEGER NOT NULL,
created_at INTEGER NOT NULL,
total decimal(10,0) NOT NULL,
PRIMARY KEY (id)
);
......@@ -94,9 +94,9 @@ INSERT INTO tbl_item (name, category_id) VALUES ('Ice Age', 2);
INSERT INTO tbl_item (name, category_id) VALUES ('Toy Story', 2);
INSERT INTO tbl_item (name, category_id) VALUES ('Cars', 2);
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (1, 1325282384, 110.0);
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (2, 1325334482, 33.0);
INSERT INTO tbl_order (customer_id, create_time, total) VALUES (2, 1325502201, 40.0);
INSERT INTO tbl_order (customer_id, created_at, total) VALUES (1, 1325282384, 110.0);
INSERT INTO tbl_order (customer_id, created_at, total) VALUES (2, 1325334482, 33.0);
INSERT INTO tbl_order (customer_id, created_at, total) VALUES (2, 1325502201, 40.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (1, 1, 1, 30.0);
INSERT INTO tbl_order_item (order_id, item_id, quantity, subtotal) VALUES (1, 2, 2, 40.0);
......
......@@ -180,13 +180,13 @@ class BaseOAuthTest extends TestCase
*/
public function determineContentTypeByRawDataProvider()
{
return array(
return [
['{name: value}', 'json'],
['name=value', 'urlencoded'],
['name1=value1&name2=value2', 'urlencoded'],
['<?xml version="1.0" encoding="UTF-8"?><tag>Value</tag>', 'xml'],
['<tag>Value</tag>', 'xml'],
);
];
}
/**
......@@ -248,4 +248,4 @@ class BaseOAuthTest extends TestCase
$this->assertEquals($expectedApiFullUrl, $oauthClient->api($apiSubUrl));
}
}
\ No newline at end of file
}
......@@ -100,15 +100,15 @@ class ActiveRecordTest extends ElasticSearchTestCase
$order = new Order();
$order->id = 1;
$order->setAttributes(['customer_id' => 1, 'create_time' => 1325282384, 'total' => 110.0], false);
$order->setAttributes(['customer_id' => 1, 'created_at' => 1325282384, 'total' => 110.0], false);
$order->save(false);
$order = new Order();
$order->id = 2;
$order->setAttributes(['customer_id' => 2, 'create_time' => 1325334482, 'total' => 33.0], false);
$order->setAttributes(['customer_id' => 2, 'created_at' => 1325334482, 'total' => 33.0], false);
$order->save(false);
$order = new Order();
$order->id = 3;
$order->setAttributes(['customer_id' => 2, 'create_time' => 1325502201, 'total' => 40.0], false);
$order->setAttributes(['customer_id' => 2, 'created_at' => 1325502201, 'total' => 40.0], false);
$order->save(false);
$orderItem = new OrderItem();
......@@ -239,14 +239,14 @@ class ActiveRecordTest extends ElasticSearchTestCase
$orders = $customer->orders;
$this->assertEquals(2, count($orders));
$orders = $customer->getOrders()->where(['between', 'create_time', 1325334000, 1325400000])->all();
$orders = $customer->getOrders()->where(['between', 'created_at', 1325334000, 1325400000])->all();
$this->assertEquals(1, count($orders));
$this->assertEquals(2, $orders[0]->id);
}
public function testFindEagerViaRelation()
{
$orders = Order::find()->with('items')->orderBy('create_time')->all();
$orders = Order::find()->with('items')->orderBy('created_at')->all();
$this->assertEquals(3, count($orders));
$order = $orders[0];
$this->assertEquals(1, $order->id);
......@@ -529,4 +529,4 @@ class ActiveRecordTest extends ElasticSearchTestCase
// TODO test AR with not mapped PK
}
\ No newline at end of file
}
......@@ -63,13 +63,13 @@ class ActiveRecordTest extends RedisTestCase
$item->save(false);
$order = new Order();
$order->setAttributes(['customer_id' => 1, 'create_time' => 1325282384, 'total' => 110.0], false);
$order->setAttributes(['customer_id' => 1, 'created_at' => 1325282384, 'total' => 110.0], false);
$order->save(false);
$order = new Order();
$order->setAttributes(['customer_id' => 2, 'create_time' => 1325334482, 'total' => 33.0], false);
$order->setAttributes(['customer_id' => 2, 'created_at' => 1325334482, 'total' => 33.0], false);
$order->save(false);
$order = new Order();
$order->setAttributes(['customer_id' => 2, 'create_time' => 1325502201, 'total' => 40.0], false);
$order->setAttributes(['customer_id' => 2, 'created_at' => 1325502201, 'total' => 40.0], false);
$order->save(false);
$orderItem = new OrderItem();
......@@ -228,4 +228,4 @@ class ActiveRecordTest extends RedisTestCase
$this->assertNull(OrderItem::find($pk));
$this->assertNotNull(OrderItem::find(['order_id' => 2, 'item_id' => 10]));
}
}
\ No newline at end of file
}
......@@ -35,14 +35,14 @@ class RedisConnectionTest extends RedisTestCase
public function keyValueData()
{
return array(
array(123),
array(-123),
array(0),
array('test'),
array("test\r\ntest"),
array(''),
);
return [
[123],
[-123],
[0],
['test'],
["test\r\ntest"],
[''],
];
}
/**
......@@ -55,4 +55,4 @@ class RedisConnectionTest extends RedisTestCase
$db->set('hi', $data);
$this->assertEquals($data, $db->get('hi'));
}
}
\ No newline at end of file
}
......@@ -502,7 +502,7 @@ trait ActiveRecordTestTrait
/*
Item (name, category_id)
Order (customer_id, create_time, total)
Order (customer_id, created_at, total)
OrderItem (order_id, item_id, quantity, subtotal)
Result should be the following:
......@@ -530,7 +530,7 @@ trait ActiveRecordTestTrait
- itemsInOrder:
Item 3: 'Ice Age', 2
*/
$orders = $this->callOrderFind()->with('itemsInOrder1')->orderBy('create_time')->all();
$orders = $this->callOrderFind()->with('itemsInOrder1')->orderBy('created_at')->all();
$this->assertEquals(3, count($orders));
$order = $orders[0];
......@@ -558,7 +558,7 @@ trait ActiveRecordTestTrait
// different order in via table
public function testFindEagerViaRelationPreserveOrderB()
{
$orders = $this->callOrderFind()->with('itemsInOrder2')->orderBy('create_time')->all();
$orders = $this->callOrderFind()->with('itemsInOrder2')->orderBy('created_at')->all();
$this->assertEquals(3, count($orders));
$order = $orders[0];
......
......@@ -41,8 +41,8 @@ class AutoTimestampTest extends TestCase
$columns = [
'id' => 'pk',
'create_time' => 'integer',
'update_time' => 'integer',
'created_at' => 'integer',
'updated_at' => 'integer',
];
Yii::$app->getDb()->createCommand()->createTable('test_auto_timestamp', $columns)->execute();
}
......@@ -62,8 +62,8 @@ class AutoTimestampTest extends TestCase
$model = new ActiveRecordAutoTimestamp();
$model->save(false);
$this->assertTrue($model->create_time >= $currentTime);
$this->assertTrue($model->update_time >= $currentTime);
$this->assertTrue($model->created_at >= $currentTime);
$this->assertTrue($model->updated_at >= $currentTime);
}
/**
......@@ -78,12 +78,12 @@ class AutoTimestampTest extends TestCase
$enforcedTime = $currentTime - 100;
$model->create_time = $enforcedTime;
$model->update_time = $enforcedTime;
$model->created_at = $enforcedTime;
$model->updated_at = $enforcedTime;
$model->save(false);
$this->assertEquals($enforcedTime, $model->create_time, 'Create time has been set on update!');
$this->assertTrue($model->update_time >= $currentTime, 'Update time has NOT been set on update!');
$this->assertEquals($enforcedTime, $model->created_at, 'Create time has been set on update!');
$this->assertTrue($model->updated_at >= $currentTime, 'Update time has NOT been set on update!');
}
}
......@@ -91,8 +91,8 @@ class AutoTimestampTest extends TestCase
* Test Active Record class with [[AutoTimestamp]] behavior attached.
*
* @property integer $id
* @property integer $create_time
* @property integer $update_time
* @property integer $created_at
* @property integer $updated_at
*/
class ActiveRecordAutoTimestamp extends ActiveRecord
{
......@@ -102,8 +102,8 @@ class ActiveRecordAutoTimestamp extends ActiveRecord
'timestamp' => [
'class' => AutoTimestamp::className(),
'attributes' => [
static::EVENT_BEFORE_INSERT => ['create_time', 'update_time'],
static::EVENT_BEFORE_UPDATE => 'update_time',
static::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
static::EVENT_BEFORE_UPDATE => 'updated_at',
],
],
];
......
<?php
namespace yiiunit\framework\console\controllers;
use yii\helpers\StringHelper;
use yiiunit\TestCase;
use yii\console\controllers\AssetController;
use Yii;
/**
* Unit test for [[\yii\console\controllers\AssetController]].
......@@ -92,11 +96,12 @@ class AssetControllerTest extends TestCase
*/
protected function createCompressConfig(array $bundles)
{
$className = $this->declareAssetBundleClass(['class' => 'AssetBundleAll']);
$baseUrl = '/test';
$config = [
'bundles' => $this->createBundleConfig($bundles),
'bundles' => $bundles,
'targets' => [
'all' => [
$className => [
'basePath' => $this->testAssetsBasePath,
'baseUrl' => $baseUrl,
'js' => 'all.js',
......@@ -112,28 +117,10 @@ class AssetControllerTest extends TestCase
}
/**
* Creates test bundle configuration.
* @param array[] $bundles asset bundles config.
* @return array bundle config.
*/
protected function createBundleConfig(array $bundles)
{
foreach ($bundles as $name => $config) {
if (!array_key_exists('basePath', $config)) {
$bundles[$name]['basePath'] = $this->testFilePath;
}
if (!array_key_exists('baseUrl', $config)) {
$bundles[$name]['baseUrl'] = '';
}
}
return $bundles;
}
/**
* Creates test compress config file.
* @param string $fileName output file name.
* @param array[] $bundles asset bundles config.
* @throws Exception on failure.
* @throws \Exception on failure.
*/
protected function createCompressConfigFile($fileName, array $bundles)
{
......@@ -147,7 +134,7 @@ class AssetControllerTest extends TestCase
* Creates test asset file.
* @param string $fileRelativeName file name relative to [[testFilePath]]
* @param string $content file content
* @throws Exception on failure.
* @throws \Exception on failure.
*/
protected function createAssetSourceFile($fileRelativeName, $content)
{
......@@ -178,7 +165,7 @@ class AssetControllerTest extends TestCase
protected function invokeAssetControllerMethod($methodName, array $args = [])
{
$controller = $this->createAssetController();
$controllerClassReflection = new ReflectionClass(get_class($controller));
$controllerClassReflection = new \ReflectionClass(get_class($controller));
$methodReflection = $controllerClassReflection->getMethod($methodName);
$methodReflection->setAccessible(true);
$result = $methodReflection->invokeArgs($controller, $args);
......@@ -186,6 +173,60 @@ class AssetControllerTest extends TestCase
return $result;
}
/**
* Composes asset bundle class source code.
* @param array $config asset bundle config.
* @return string class source code.
*/
protected function composeAssetBundleClassSource(array &$config)
{
$config = array_merge(
[
'namespace' => StringHelper::dirname(get_class($this)),
'class' => 'AppAsset',
'basePath' => $this->testFilePath,
'baseUrl' => '',
'css' => [],
'js' => [],
'depends' => [],
],
$config
);
foreach ($config as $name => $value) {
if (is_array($value)) {
$config[$name] = var_export($value, true);
}
}
$source = <<<EOL
namespace {$config['namespace']};
use yii\web\AssetBundle;
class {$config['class']} extends AssetBundle
{
public \$basePath = '{$config['basePath']}';
public \$baseUrl = '{$config['baseUrl']}';
public \$css = {$config['css']};
public \$js = {$config['js']};
public \$depends = {$config['depends']};
}
EOL;
return $source;
}
/**
* Declares asset bundle class according to given configuration.
* @param array $config asset bundle config.
* @return string new class full name.
*/
protected function declareAssetBundleClass(array $config)
{
$sourceCode = $this->composeAssetBundleClassSource($config);
eval($sourceCode);
return $config['namespace'] . '\\' . $config['class'];
}
// Tests :
public function testActionTemplate()
......@@ -195,7 +236,7 @@ class AssetControllerTest extends TestCase
$this->assertTrue(file_exists($configFileName), 'Unable to create config file template!');
}
public function atestActionCompress()
public function testActionCompress()
{
// Given :
$cssFiles = [
......@@ -219,15 +260,13 @@ class AssetControllerTest extends TestCase
}",
];
$this->createAssetSourceFiles($jsFiles);
$assetBundleClassName = $this->declareAssetBundleClass([
'css' => array_keys($cssFiles),
'js' => array_keys($jsFiles),
]);
$bundles = [
'app' => [
'css' => array_keys($cssFiles),
'js' => array_keys($jsFiles),
'depends' => [
'yii',
],
],
$assetBundleClassName
];
$bundleFile = $this->testFilePath . DIRECTORY_SEPARATOR . 'bundle.php';
......@@ -299,6 +338,18 @@ class AssetControllerTest extends TestCase
'/test/base/path/assets/output',
'.absolute-url-secure-class {background-image: url(https://secure.domain.com/img/image.gif);}',
],
[
"@font-face {
src: url('../fonts/glyphicons-halflings-regular.eot');
src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype');
}",
'/test/base/path/assets/input/css',
'/test/base/path/assets/output',
"@font-face {
src: url('../input/fonts/glyphicons-halflings-regular.eot');
src: url('../input/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype');
}",
],
];
}
......
......@@ -58,7 +58,7 @@ class ActiveRecordTest extends DatabaseTestCase
public function testFindScalar()
{
// query scalar
$customerName = $this->callCustomerFind()->where(array('id' => 2))->select('name')->scalar();
$customerName = $this->callCustomerFind()->where(['id' => 2])->select('name')->scalar();
$this->assertEquals('user2', $customerName);
}
......
......@@ -84,7 +84,7 @@ class SqliteQueryBuilderTest extends QueryBuilderTest
public function testBatchInsert()
{
$sql = $this->getQueryBuilder()->batchInsert('{{tbl_customer}} t', ['t.id','t.name'], array(array(1,'a'), array(2,'b')));
$sql = $this->getQueryBuilder()->batchInsert('{{tbl_customer}} t', ['t.id','t.name'], [[1,'a'], [2,'b']]);
$this->assertEquals("INSERT INTO {{tbl_customer}} t (`t`.`id`, `t`.`name`) SELECT 1, 'a' UNION ALL 2, 'b'", $sql);
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yiiunit\framework\test;
use yii\test\ActiveFixture;
use yiiunit\data\ar\ActiveRecord;
use yii\test\FixtureTrait;
use yii\test\InitDbFixture;
use yiiunit\data\ar\Customer;
use yiiunit\framework\db\DatabaseTestCase;
class CustomerFixture extends ActiveFixture
{
public $modelClass = 'yiiunit\data\ar\Customer';
}
class MyDbTestCase
{
use FixtureTrait;
public function setUp()
{
$this->loadFixtures();
}
public function tearDown()
{
$this->unloadFixtures();
}
protected function fixtures()
{
return [
'customers' => CustomerFixture::className(),
];
}
protected function globalFixtures()
{
return [
InitDbFixture::className(),
];
}
}
/**
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class ActiveFixtureTest extends DatabaseTestCase
{
public function setUp()
{
parent::setUp();
\Yii::$app->setComponent('db', $this->getConnection());
ActiveRecord::$db = $this->getConnection();
}
public function tearDown()
{
parent::tearDown();
}
public function testGetData()
{
$test = new MyDbTestCase();
$test->setUp();
$fixture = $test->customers;
$this->assertEquals(CustomerFixture::className(), get_class($fixture));
$this->assertEquals(2, count($fixture));
$this->assertEquals(1, $fixture['customer1']['id']);
$this->assertEquals('customer1@example.com', $fixture['customer1']['email']);
$this->assertEquals(2, $fixture['customer2']['id']);
$this->assertEquals('customer2@example.com', $fixture['customer2']['email']);
}
public function testGetModel()
{
$test = new MyDbTestCase();
$test->setUp();
$fixture = $test->customers;
$this->assertEquals(Customer::className(), get_class($fixture->getModel('customer1')));
$this->assertEquals(1, $fixture->getModel('customer1')->id);
$this->assertEquals('customer1@example.com', $fixture->getModel('customer1')->email);
$this->assertEquals(2, $fixture->getModel('customer2')->id);
$this->assertEquals('customer2@example.com', $fixture->getModel('customer2')->email);
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yiiunit\framework\test;
use yii\test\Fixture;
use yii\test\FixtureTrait;
use yiiunit\TestCase;
class Fixture1 extends Fixture
{
public $depends = ['yiiunit\framework\test\Fixture2'];
public function load()
{
MyTestCase::$load .= '1';
}
public function unload()
{
MyTestCase::$unload .= '1';
}
}
class Fixture2 extends Fixture
{
public $depends = ['yiiunit\framework\test\Fixture3'];
public function load()
{
MyTestCase::$load .= '2';
}
public function unload()
{
MyTestCase::$unload .= '2';
}
}
class Fixture3 extends Fixture
{
public function load()
{
MyTestCase::$load .= '3';
}
public function unload()
{
MyTestCase::$unload .= '3';
}
}
class MyTestCase
{
use FixtureTrait;
public $scenario = 1;
public static $load;
public static $unload;
public function setUp()
{
$this->loadFixtures();
}
public function tearDown()
{
$this->unloadFixtures();
}
public function fetchFixture($name)
{
return $this->getFixture($name);
}
protected function fixtures()
{
switch ($this->scenario) {
case 0: return [];
case 1: return [
'fixture1' => Fixture1::className(),
];
case 2: return [
'fixture2' => Fixture2::className(),
];
case 3: return [
'fixture3' => Fixture3::className(),
];
case 4: return [
'fixture1' => Fixture1::className(),
'fixture2' => Fixture2::className(),
];
case 5: return [
'fixture2' => Fixture2::className(),
'fixture3' => Fixture3::className(),
];
case 6: return [
'fixture1' => Fixture1::className(),
'fixture3' => Fixture3::className(),
];
case 7:
default: return [
'fixture1' => Fixture1::className(),
'fixture2' => Fixture2::className(),
'fixture3' => Fixture3::className(),
];
}
}
}
class FixtureTest extends TestCase
{
public function testDependencies()
{
foreach ($this->getDependencyTests() as $scenario => $result) {
$test = new MyTestCase();
$test->scenario = $scenario;
$test->setUp();
foreach ($result as $name => $loaded) {
$this->assertEquals($loaded, $test->fetchFixture($name) !== null, "Verifying scenario $scenario fixture $name");
}
}
}
public function testLoadSequence()
{
foreach ($this->getLoadSequenceTests() as $scenario => $result) {
$test = new MyTestCase();
$test->scenario = $scenario;
MyTestCase::$load = '';
MyTestCase::$unload = '';
$test->setUp();
$this->assertEquals($result[0], MyTestCase::$load, "Verifying scenario $scenario load sequence");
$test->tearDown();
$this->assertEquals($result[1], MyTestCase::$unload, "Verifying scenario $scenario unload sequence");
}
}
protected function getDependencyTests()
{
return [
0 => ['fixture1' => false, 'fixture2' => false, 'fixture3' => false],
1 => ['fixture1' => true, 'fixture2' => false, 'fixture3' => false],
2 => ['fixture1' => false, 'fixture2' => true, 'fixture3' => false],
3 => ['fixture1' => false, 'fixture2' => false, 'fixture3' => true],
4 => ['fixture1' => true, 'fixture2' => true, 'fixture3' => false],
5 => ['fixture1' => false, 'fixture2' => true, 'fixture3' => true],
6 => ['fixture1' => true, 'fixture2' => false, 'fixture3' => true],
7 => ['fixture1' => true, 'fixture2' => true, 'fixture3' => true],
];
}
protected function getLoadSequenceTests()
{
return [
0 => ['', ''],
1 => ['321', '123'],
2 => ['32', '23'],
3 => ['3', '3'],
4 => ['321', '123'],
5 => ['32', '23'],
6 => ['321', '123'],
7 => ['321', '123'],
];
}
}
<?php
return [
'customer1' => [
'email' => 'customer1@example.com',
'name' => 'customer1',
'address' => 'address1',
'status' => 1,
],
'customer2' => [
'email' => 'customer2@example.com',
'name' => 'customer2',
'address' => 'address2',
'status' => 2,
],
];
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