Browse Source

Merge branch 'master' into elasticsearch

* master: (157 commits)
  Fix phpdoc.
  fixed test break.
  refactor Exception::toArrayRecursive().
  Fix StringHelper::substr() call.
  Incorrect array representation when has previous
  Add data padding and key derivation.
  Fixed beforeCopy option.
  doc fix.
  Added missing beforeCopy option to FileHelper::copyDirectory
  Renamed ListViewBase → BaseListView
  Fixes #915: helper classes renamed again
  Renamed YiiBase to AbstractYii
  reverted doc change: the original one is correct.
  Renamed base helper classes
  Advanced application: added applying migrations to readme
  fixed typos
  Advanced application: init is now automatically called when installing via Composer
  Advanced application: init script can now be executed in non-interactive input mode
  Advanced application: fixed console return codes
  Fixed phpdoc
  ...
tags/2.0.0-alpha
Carsten Brandt 11 years ago
parent
commit
91c1805ed7
  1. 3
      .gitignore
  2. 14
      .travis.yml
  3. 6
      apps/advanced/README.md
  4. 8
      apps/advanced/backend/config/main.php
  5. 30
      apps/advanced/backend/controllers/SiteController.php
  6. 29
      apps/advanced/backend/views/site/error.php
  7. 7
      apps/advanced/common/models/User.php
  8. 3
      apps/advanced/composer.json
  9. 12
      apps/advanced/environments/dev/backend/config/main-local.php
  10. 2
      apps/advanced/environments/dev/backend/web/index.php
  11. 12
      apps/advanced/environments/dev/frontend/config/main-local.php
  12. 3
      apps/advanced/environments/dev/frontend/web/index.php
  13. 4
      apps/advanced/environments/dev/yii
  14. 2
      apps/advanced/environments/prod/backend/web/index.php
  15. 3
      apps/advanced/environments/prod/frontend/web/index.php
  16. 4
      apps/advanced/environments/prod/yii
  17. 9
      apps/advanced/frontend/config/main.php
  18. 28
      apps/advanced/frontend/controllers/SiteController.php
  19. 2
      apps/advanced/frontend/views/emails/passwordResetToken.php
  20. 29
      apps/advanced/frontend/views/site/error.php
  21. 8
      apps/advanced/frontend/widgets/Alert.php
  22. 72
      apps/advanced/init
  23. 2
      apps/basic/README.md
  24. 7
      apps/basic/config/web.php
  25. 30
      apps/basic/controllers/SiteController.php
  26. 2
      apps/basic/models/User.php
  27. 4
      apps/basic/views/layouts/main.php
  28. 41
      apps/basic/views/site/login.php
  29. 2
      apps/benchmark/README.md
  30. 2
      build/build.xml
  31. 2
      build/controllers/ClassmapController.php
  32. 2
      build/controllers/PhpDocController.php
  33. 102
      docs/guide/active-record.md
  34. 11
      docs/guide/bootstrap-widgets.md
  35. 17
      docs/guide/database-basics.md
  36. 14
      docs/guide/model.md
  37. 20
      docs/guide/overview.md
  38. 7
      docs/guide/security.md
  39. 39
      docs/guide/template.md
  40. 6
      docs/guide/upgrade-from-v1.md
  41. 188
      docs/guide/validation.md
  42. 2
      docs/internals/autoloader.md
  43. 10
      extensions/composer/README.md
  44. 10
      framework/yii/BaseYii.php
  45. 8
      framework/yii/Yii.php
  46. 148
      framework/yii/assets/yii.js
  47. 14
      framework/yii/base/Component.php
  48. 2
      framework/yii/base/Controller.php
  49. 2
      framework/yii/base/ErrorHandler.php
  50. 21
      framework/yii/base/Exception.php
  51. 51
      framework/yii/base/Model.php
  52. 7
      framework/yii/base/Module.php
  53. 9
      framework/yii/base/Object.php
  54. 7
      framework/yii/base/View.php
  55. 4
      framework/yii/bootstrap/Nav.php
  56. 29
      framework/yii/caching/MemCache.php
  57. 10
      framework/yii/caching/RedisCache.php
  58. 41
      framework/yii/classes.php
  59. 1
      framework/yii/data/ActiveDataProvider.php
  60. 1
      framework/yii/data/ArrayDataProvider.php
  61. 5
      framework/yii/data/DataProvider.php
  62. 4
      framework/yii/data/DataProviderInterface.php
  63. 4
      framework/yii/data/Pagination.php
  64. 53
      framework/yii/db/ActiveRecord.php
  65. 4
      framework/yii/db/ActiveRelation.php
  66. 6
      framework/yii/db/Command.php
  67. 5
      framework/yii/db/Connection.php
  68. 2
      framework/yii/db/DataReader.php
  69. 17
      framework/yii/db/QueryBuilder.php
  70. 3
      framework/yii/db/Schema.php
  71. 6
      framework/yii/db/TableSchema.php
  72. 117
      framework/yii/db/cubrid/QueryBuilder.php
  73. 240
      framework/yii/db/cubrid/Schema.php
  74. 1
      framework/yii/db/mssql/QueryBuilder.php
  75. 5
      framework/yii/db/mssql/Schema.php
  76. 23
      framework/yii/db/mssql/TableSchema.php
  77. 3
      framework/yii/db/mysql/QueryBuilder.php
  78. 4
      framework/yii/db/mysql/Schema.php
  79. 3
      framework/yii/db/pgsql/QueryBuilder.php
  80. 33
      framework/yii/db/pgsql/Schema.php
  81. 1
      framework/yii/db/sqlite/QueryBuilder.php
  82. 8
      framework/yii/db/sqlite/Schema.php
  83. 1
      framework/yii/debug/assets/main.css
  84. 22
      framework/yii/gii/Generator.php
  85. 33
      framework/yii/gii/controllers/DefaultController.php
  86. 2
      framework/yii/gii/generators/controller/Generator.php
  87. 325
      framework/yii/gii/generators/crud/Generator.php
  88. 8
      framework/yii/gii/generators/crud/form.php
  89. 154
      framework/yii/gii/generators/crud/templates/controller.php
  90. 8
      framework/yii/gii/generators/crud/templates/model.php
  91. 83
      framework/yii/gii/generators/crud/templates/search.php
  92. 44
      framework/yii/gii/generators/crud/templates/views/_form.php
  93. 45
      framework/yii/gii/generators/crud/templates/views/_search.php
  94. 33
      framework/yii/gii/generators/crud/templates/views/create.php
  95. 73
      framework/yii/gii/generators/crud/templates/views/index.php
  96. 36
      framework/yii/gii/generators/crud/templates/views/update.php
  97. 53
      framework/yii/gii/generators/crud/templates/views/view.php
  98. 7
      framework/yii/gii/generators/model/Generator.php
  99. 3
      framework/yii/gii/views/default/view.php
  100. 102
      framework/yii/grid/ActionColumn.php
  101. Some files were not shown because too many files have changed in this diff Show More

3
.gitignore vendored

@ -17,3 +17,6 @@ Thumbs.db
# composer itself is not needed
composer.phar
# Mac DS_Store Files
.DS_Store

14
.travis.yml

@ -5,14 +5,24 @@ php:
- 5.4
- 5.5
env:
- CUBRID_VERSION=9.1.0
services:
- redis-server
- memcached
before_script:
- composer self-update && composer --version
- composer require satooshi/php-coveralls 0.6.*
- mysql -e 'CREATE DATABASE yiitest;';
- psql -U postgres -c 'CREATE DATABASE yiitest;';
- tests/unit/data/travis/apc-setup.sh
- tests/unit/data/travis/memcache-setup.sh
- tests/unit/data/travis/cubrid-setup.sh
script:
- phpunit --coverage-clover tests/unit/runtime/coveralls/clover.xml
- phpunit --coverage-clover tests/unit/runtime/coveralls/clover.xml --verbose --exclude-group mssql,oci,wincache,xcache,zenddata
after_script:
- php vendor/bin/coveralls
- php vendor/bin/coveralls

6
apps/advanced/README.md

@ -74,6 +74,8 @@ You can then install the application using the following command:
php composer.phar create-project --stability=dev yiisoft/yii2-app-advanced yii-advanced
~~~
Note that in order to install some dependencies you must have `php_openssl` extension enabled.
### Install from an Archive File
@ -101,9 +103,11 @@ GETTING STARTED
After you install the application, you have to conduct the following steps to initialize
the installed application. You only need to do these once for all.
1. Execute the `init` command and select `dev` as environment.
1. Execute the `init` command and select `dev` as environment. Alternatively you can execute it as `init --env=Development`
or `init --env=Production`.
2. Create a new database. It is assumed that MySQL InnoDB is used. If not, adjust `console/migrations/m130524_201442_init.php`.
3. In `common/config/params.php` set your database details in `components.db` values.
4. Apply migrations with `yii migrate`.
Now you should be able to access:

8
apps/advanced/backend/config/main.php

@ -17,13 +17,16 @@ return array(
'modules' => array(
),
'components' => array(
'request' => array(
'enableCsrfValidation' => true,
),
'db' => $params['components.db'],
'cache' => $params['components.cache'],
'user' => array(
'class' => 'yii\web\User',
'identityClass' => 'common\models\User',
),
'log' => array(
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => array(
array(
'class' => 'yii\log\FileTarget',
@ -31,6 +34,9 @@ return array(
),
),
),
'errorHandler' => array(
'errorAction' => 'site/error',
),
),
'params' => $params,
);

30
apps/advanced/backend/controllers/SiteController.php

@ -8,6 +8,36 @@ use common\models\LoginForm;
class SiteController extends Controller
{
public function behaviors()
{
return array(
'access' => array(
'class' => \yii\web\AccessControl::className(),
'rules' => array(
array(
'actions' => array('login'),
'allow' => true,
'roles' => array('?'),
),
array(
'actions' => array('logout', 'index'),
'allow' => true,
'roles' => array('@'),
),
),
),
);
}
public function actions()
{
return array(
'error' => array(
'class' => 'yii\web\ErrorAction',
),
);
}
public function actionIndex()
{
return $this->render('index');

29
apps/advanced/backend/views/site/error.php

@ -0,0 +1,29 @@
<?php
use yii\helpers\Html;
/**
* @var yii\base\View $this
* @var string $name
* @var string $message
* @var Exception $exception
*/
$this->title = $name;
?>
<div class="site-error">
<h1><?php echo Html::encode($this->title); ?></h1>
<div class="alert alert-danger">
<?php echo nl2br(Html::encode($message)); ?>
</div>
<p>
The above error occurred while the Web server was processing your request.
</p>
<p>
Please contact us if you think this is a server error. Thank you.
</p>
</div>

7
apps/advanced/common/models/User.php

@ -3,7 +3,7 @@ namespace common\models;
use yii\db\ActiveRecord;
use yii\helpers\Security;
use yii\web\Identity;
use yii\web\IdentityInterface;
/**
* Class User
@ -20,7 +20,7 @@ use yii\web\Identity;
* @property integer $create_time
* @property integer $update_time
*/
class User extends ActiveRecord implements Identity
class User extends ActiveRecord implements IdentityInterface
{
/**
* @var string the raw password. Used to collect password input and isn't saved in database
@ -49,7 +49,7 @@ class User extends ActiveRecord implements Identity
* Finds an identity by the given ID.
*
* @param string|integer $id the ID to be looked for
* @return Identity|null the identity object that matches the given ID.
* @return IdentityInterface|null the identity object that matches the given ID.
*/
public static function findIdentity($id)
{
@ -123,7 +123,6 @@ class User extends ActiveRecord implements Identity
{
return array(
'signup' => array('username', 'email', 'password'),
'login' => array('username', 'password'),
'resetPassword' => array('password'),
'requestPasswordResetToken' => array('email'),
);

3
apps/advanced/composer.json

@ -20,7 +20,8 @@
},
"scripts": {
"post-create-project-cmd": [
"yii\\composer\\InstallHandler::setPermissions"
"yii\\composer\\InstallHandler::setPermissions",
"./init"
]
},
"extra": {

12
apps/advanced/environments/dev/backend/config/main-local.php

@ -1,17 +1,11 @@
<?php
return array(
'preload' => array(
//'debug',
),
'modules' => array(
// 'debug' => array(
// 'class' => 'yii\debug\Module',
// ),
),
'components' => array(
'log' => array(
'targets' => array(
// array(
// 'class' => 'yii\log\DebugTarget',
// )
),
),
),
);

2
apps/advanced/environments/dev/backend/web/index.php

@ -1,6 +1,6 @@
<?php
// comment out the following line to disable debug mode
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
require(__DIR__ . '/../../vendor/autoload.php');
require(__DIR__ . '/../../vendor/yiisoft/yii2/yii/Yii.php');

12
apps/advanced/environments/dev/frontend/config/main-local.php

@ -1,17 +1,11 @@
<?php
return array(
'preload' => array(
//'debug',
),
'modules' => array(
// 'debug' => array(
// 'class' => 'yii\debug\Module',
// ),
),
'components' => array(
'log' => array(
'targets' => array(
// array(
// 'class' => 'yii\log\DebugTarget',
// )
),
),
),
);

3
apps/advanced/environments/dev/frontend/web/index.php

@ -1,7 +1,6 @@
<?php
// comment out the following line to disable debug mode
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
require(__DIR__ . '/../../vendor/autoload.php');
require(__DIR__ . '/../../vendor/yiisoft/yii2/yii/Yii.php');

4
apps/advanced/environments/dev/yii

@ -9,6 +9,7 @@
*/
defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_ENV') or define('YII_ENV', 'dev');
// fcgi doesn't have STDIN defined by default
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
@ -23,4 +24,5 @@ $config = yii\helpers\ArrayHelper::merge(
);
$application = new yii\console\Application($config);
return $application->run();
$exitCode = $application->run();
exit($exitCode);

2
apps/advanced/environments/prod/backend/web/index.php

@ -1,6 +1,6 @@
<?php
// comment out the following line to disable debug mode
defined('YII_DEBUG') or define('YII_DEBUG', false);
defined('YII_ENV') or define('YII_ENV', 'prod');
require(__DIR__ . '/../../vendor/autoload.php');
require(__DIR__ . '/../../vendor/yiisoft/yii2/yii/Yii.php');

3
apps/advanced/environments/prod/frontend/web/index.php

@ -1,7 +1,6 @@
<?php
// comment out the following line to disable debug mode
defined('YII_DEBUG') or define('YII_DEBUG', false);
defined('YII_ENV') or define('YII_ENV', 'prod');
require(__DIR__ . '/../../vendor/autoload.php');
require(__DIR__ . '/../../vendor/yiisoft/yii2/yii/Yii.php');

4
apps/advanced/environments/prod/yii

@ -9,6 +9,7 @@
*/
defined('YII_DEBUG') or define('YII_DEBUG', false);
defined('YII_ENV') or define('YII_ENV', 'prod');
// fcgi doesn't have STDIN defined by default
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
@ -23,4 +24,5 @@ $config = yii\helpers\ArrayHelper::merge(
);
$application = new yii\console\Application($config);
return $application->run();
$exitCode = $application->run();
exit($exitCode);

9
apps/advanced/frontend/config/main.php

@ -14,15 +14,19 @@ return array(
'vendorPath' => dirname(dirname(__DIR__)) . '/vendor',
'controllerNamespace' => 'frontend\controllers',
'modules' => array(
'gii' => 'yii\gii\Module'
),
'components' => array(
'request' => array(
'enableCsrfValidation' => true,
),
'db' => $params['components.db'],
'cache' => $params['components.cache'],
'user' => array(
'class' => 'yii\web\User',
'identityClass' => 'common\models\User',
),
'log' => array(
'traceLevel' => YII_DEBUG ? 3 : 0,
'targets' => array(
array(
'class' => 'yii\log\FileTarget',
@ -30,6 +34,9 @@ return array(
),
),
),
'errorHandler' => array(
'errorAction' => 'site/error',
),
),
'params' => $params,
);

28
apps/advanced/frontend/controllers/SiteController.php

@ -12,11 +12,37 @@ use yii\helpers\Security;
class SiteController extends Controller
{
public function behaviors()
{
return array(
'access' => array(
'class' => \yii\web\AccessControl::className(),
'only' => array('login', 'logout', 'signup'),
'rules' => array(
array(
'actions' => array('login', 'signup'),
'allow' => true,
'roles' => array('?'),
),
array(
'actions' => array('logout'),
'allow' => true,
'roles' => array('@'),
),
),
),
);
}
public function actions()
{
return array(
'error' => array(
'class' => 'yii\web\ErrorAction',
),
'captcha' => array(
'class' => 'yii\captcha\CaptchaAction',
'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
),
);
}
@ -138,7 +164,7 @@ class SiteController extends Controller
$headers = "From: $name <{$fromEmail}>\r\n" .
"MIME-Version: 1.0\r\n" .
"Content-type: text/plain; charset=UTF-8";
return mail($fromEmail, $subject, $body, $headers);
return mail($email, $subject, $body, $headers);
}
return false;

2
apps/advanced/frontend/views/emails/passwordResetToken.php

@ -13,4 +13,4 @@ Hello <?php echo Html::encode($user->username)?>,
Follow the link below to reset your password:
<?php Html::a(Html::encode($resetLink), $resetLink)?>
<?php echo Html::a(Html::encode($resetLink), $resetLink)?>

29
apps/advanced/frontend/views/site/error.php

@ -0,0 +1,29 @@
<?php
use yii\helpers\Html;
/**
* @var yii\base\View $this
* @var string $name
* @var string $message
* @var Exception $exception
*/
$this->title = $name;
?>
<div class="site-error">
<h1><?php echo Html::encode($this->title); ?></h1>
<div class="alert alert-danger">
<?php echo nl2br(Html::encode($message)); ?>
</div>
<p>
The above error occurred while the Web server was processing your request.
</p>
<p>
Please contact us if you think this is a server error. Thank you.
</p>
</div>

8
apps/advanced/frontend/widgets/Alert.php

@ -23,13 +23,13 @@ class Alert extends \yii\bootstrap\Alert
private $_doNotRender = false;
public function init()
{
if ($this->body = \Yii::$app->getSession()->getFlash('error')) {
if ($this->body = \Yii::$app->getSession()->getFlash('error', null, true)) {
Html::addCssClass($this->options, 'alert-danger');
} elseif ($this->body = \Yii::$app->getSession()->getFlash('success')) {
} elseif ($this->body = \Yii::$app->getSession()->getFlash('success', null, true)) {
Html::addCssClass($this->options, 'alert-success');
} elseif ($this->body = \Yii::$app->getSession()->getFlash('info')) {
} elseif ($this->body = \Yii::$app->getSession()->getFlash('info', null, true)) {
Html::addCssClass($this->options, 'alert-info');
} elseif ($this->body = \Yii::$app->getSession()->getFlash('warning')) {
} elseif ($this->body = \Yii::$app->getSession()->getFlash('warning', null, true)) {
Html::addCssClass($this->options, 'alert-warning');
} else {
$this->_doNotRender = true;

72
apps/advanced/init

@ -1,27 +1,49 @@
#!/usr/bin/env php
<?php
$params = getParams();
$root = str_replace('\\', '/', __DIR__);
$envs = require("$root/environments/index.php");
$envNames = array_keys($envs);
echo "Yii Application Init Tool v1.0\n\n";
echo "Which environment do you want the application to be initialized in?\n\n";
foreach ($envNames as $i => $name) {
echo " [$i] $name\n";
echo "Yii Application Initialization Tool v1.0\n\n";
$envName = null;
if (empty($params['env'])) {
echo "Which environment do you want the application to be initialized in?\n\n";
foreach ($envNames as $i => $name) {
echo " [$i] $name\n";
}
echo "\n Your choice [0-" . (count($envs) - 1) . ', or "q" to quit] ';
$answer = trim(fgets(STDIN));
if (!ctype_digit($answer) || !in_array($answer, range(0, count($envs) - 1))) {
echo "\n Quit initialization.\n";
exit(1);
}
if(isset($envNames[$answer])) {
$envName = $envNames[$answer];
}
}
else {
$envName = $params['env'];
}
echo "\n Your choice [0-" . (count($envs) - 1) . ', or "q" to quit] ';
$answer = trim(fgets(STDIN));
if (!ctype_digit($answer) || !isset($envNames[$answer])) {
echo "\n Quit initialization.\n";
return;
if (!in_array($envName, $envNames)) {
$envsList = implode(', ', $envNames);
echo "\n $envName is not a valid environment. Try one of the following: $envsList. \n";
exit(2);
}
$env = $envs[$envNames[$answer]];
echo "\n Initialize the application under '{$envNames[$answer]}' environment? [yes|no] ";
$answer = trim(fgets(STDIN));
if (strncasecmp($answer, 'y', 1)) {
echo "\n Quit initialization.\n";
return;
$env = $envs[$envName];
if (empty($params['env'])) {
echo "\n Initialize the application under '{$envNames[$answer]}' environment? [yes|no] ";
$answer = trim(fgets(STDIN));
if (strncasecmp($answer, 'y', 1)) {
echo "\n Quit initialization.\n";
exit(1);
}
}
echo "\n Start initialization ...\n\n";
@ -110,3 +132,23 @@ function copyFile($root, $source, $target, &$all)
file_put_contents($root . '/' . $target, file_get_contents($root . '/' . $source));
return true;
}
function getParams()
{
$rawParams = array();
if (isset($_SERVER['argv'])) {
$rawParams = $_SERVER['argv'];
array_shift($rawParams);
}
$params = array();
foreach ($rawParams as $param) {
if (preg_match('/^--(\w+)(=(.*))?$/', $param, $matches)) {
$name = $matches[1];
$params[$name] = isset($matches[3]) ? $matches[3] : true;
} else {
$params[] = $param;
}
}
return $params;
}

2
apps/basic/README.md

@ -56,6 +56,8 @@ php composer.phar create-project --stability=dev yiisoft/yii2-app-basic yii-basi
Now you should be able to access the application using the URL `http://localhost/yii-basic/web/`,
assuming `yii-basic` is directly under the document root of your Web server.
Note that in order to install some dependencies you must have `php_openssl` extension enabled.
### Install from an Archive File

7
apps/basic/config/web.php

@ -1,9 +1,12 @@
<?php
$params = require(__DIR__ . '/params.php');
$config = array(
'id' => 'bootstrap',
'basePath' => dirname(__DIR__),
'components' => array(
'request' => array(
'enableCsrfValidation' => true,
),
'cache' => array(
'class' => 'yii\caching\FileCache',
),
@ -23,7 +26,7 @@ $config = array(
),
),
),
'params' => require(__DIR__ . '/params.php'),
'params' => $params,
);
if (YII_ENV_DEV) {

30
apps/basic/controllers/SiteController.php

@ -3,12 +3,42 @@
namespace app\controllers;
use Yii;
use yii\web\AccessControl;
use yii\web\Controller;
use yii\web\VerbFilter;
use app\models\LoginForm;
use app\models\ContactForm;
class SiteController extends Controller
{
public function behaviors()
{
return array(
'access' => array(
'class' => AccessControl::className(),
'only' => array('login', 'logout'),
'rules' => array(
array(
'actions' => array('login'),
'allow' => true,
'roles' => array('?'),
),
array(
'actions' => array('logout'),
'allow' => true,
'roles' => array('@'),
),
),
),
'verbs' => array(
'class' => VerbFilter::className(),
'actions' => array(
'logout' => array('post'),
),
),
);
}
public function actions()
{
return array(

2
apps/basic/models/User.php

@ -2,7 +2,7 @@
namespace app\models;
class User extends \yii\base\Object implements \yii\web\Identity
class User extends \yii\base\Object implements \yii\web\IdentityInterface
{
public $id;
public $username;

4
apps/basic/views/layouts/main.php

@ -36,7 +36,9 @@ app\config\AppAsset::register($this);
array('label' => 'Contact', 'url' => array('/site/contact')),
Yii::$app->user->isGuest ?
array('label' => 'Login', 'url' => array('/site/login')) :
array('label' => 'Logout (' . Yii::$app->user->identity->username .')' , 'url' => array('/site/logout')),
array('label' => 'Logout (' . Yii::$app->user->identity->username .')' ,
'url' => array('/site/logout'),
'linkOptions' => array('data-method' => 'post')),
),
));
NavBar::end();

41
apps/basic/views/site/login.php

@ -15,20 +15,33 @@ $this->params['breadcrumbs'][] = $this->title;
<p>Please fill out the following fields to login:</p>
<div class="row">
<div class="col-lg-3">
<?php $form = ActiveForm::begin(array('id' => 'login-form')); ?>
<?php echo $form->field($model, 'username'); ?>
<?php echo $form->field($model, 'password')->passwordInput(); ?>
<?php echo $form->field($model, 'rememberMe')->checkbox(); ?>
<div class="form-group">
<?php echo Html::submitButton('Login', array('class' => 'btn btn-primary')); ?>
</div>
<?php ActiveForm::end(); ?>
</div>
<div class="col-lg-5" style="color:#999;margin:1em;padding-top:60px">
You may login with <strong>admin/admin</strong> or <strong>demo/demo</strong>.<br>
To modify the username/password, please check out the code <code>app\models\User::$users</code>.
<?php $form = ActiveForm::begin(array(
'id' => 'login-form',
'options' => array('class' => 'form-horizontal'),
'fieldConfig' => array(
'template' => "{label}\n<div class=\"col-lg-3\">{input}</div>\n<div class=\"col-lg-8\">{error}</div>",
'labelOptions' => array('class' => 'col-lg-1 control-label'),
),
)); ?>
<?php echo $form->field($model, 'username'); ?>
<?php echo $form->field($model, 'password')->passwordInput(); ?>
<?php echo $form->field($model, 'rememberMe', array(
'template' => "<div class=\"col-lg-offset-1 col-lg-3\">{input}</div>\n<div class=\"col-lg-8\">{error}</div>",
))->checkbox(); ?>
<div class="form-group">
<div class="col-lg-offset-1 col-lg-11">
<?php echo Html::submitButton('Login', array('class' => 'btn btn-primary')); ?>
</div>
</div>
<?php ActiveForm::end(); ?>
<div class="col-lg-offset-1" style="color:#999;">
You may login with <strong>admin/admin</strong> or <strong>demo/demo</strong>.<br>
To modify the username/password, please check out the code <code>app\models\User::$users</code>.
</div>
</div>

2
apps/benchmark/README.md

@ -54,3 +54,5 @@ http://localhost/yii-benchmark/index.php/site/hello
In the above, we assume `yii-benchmark` is directly under the document root of your Web server.
Note that in order to install some dependencies you must have `php_openssl` extension enabled.

2
build/build.xml

@ -265,7 +265,7 @@ Please update yiisite/common/data/versions.php file with the following code:
where &lt;target name&gt; can be one of the following:
- sync : synchronize yiilite.php and YiiBase.php
- sync : synchronize yiilite.php and BaseYii.php
- message : extract i18n messages of the framework
- src : build source release
- doc : build documentation release (Windows only)

2
build/controllers/ClassmapController.php

@ -45,7 +45,7 @@ class ClassmapController extends Controller
'only' => array('.php'),
'except' => array(
'Yii.php',
'YiiBase.php',
'BaseYii.php',
'/debug/',
'/console/',
'/test/',

2
build/controllers/PhpDocController.php

@ -56,7 +56,7 @@ class PhpDocController extends Controller
},
'only' => array('.php'),
'except' => array(
'YiiBase.php',
'BaseYii.php',
'Yii.php',
'/debug/views/',
'/requirements/',

102
docs/guide/active-record.md

@ -1,17 +1,15 @@
Active Record
=============
ActiveRecord implements the [Active Record design pattern](http://en.wikipedia.org/wiki/Active_record).
The idea is that an [[ActiveRecord]] object is associated with a row in a database table and its attributes are mapped
to the columns of the corresponding table columns. Reading an ActiveRecord attribute is equivalent to accessing
the corresponding table column. For example, a `Customer` object is associated with a row in the
`tbl_customer` table, and its `name` attribute is mapped to the `name` column in the `tbl_customer` table.
To get the value of the `name` column in the table row, you can simply use the expression `$customer->name`,
just like reading an object property.
Instead of writing raw SQL statements to perform database queries, you can call intuitive methods provided
by ActiveRecord to achieve the same goals. For example, calling [[ActiveRecord::save()|save()]] would
insert or update a row in the associated table of the ActiveRecord class:
Active Record implements the [Active Record design pattern](http://en.wikipedia.org/wiki/Active_record).
The premise behind Active Record is that an individual [[ActiveRecord]] object is associated with a specific row in a database table. The object's attributes are mapped to the columns of the corresponding table. Referencing an Active Record attribute is equivalent to accessing
the corresponding table column for that record.
As an example, say that the `Customer` ActiveRecord class is associated with the
`tbl_customer` table. This would mean that the class's `name` attribute is automatically mapped to the `name` column in `tbl_customer`.
Thanks to Active Record, assuming the variable `$customer` is an object of type `Customer`, to get the value of the `name` column for the table row, you can use the expression `$customer->name`. In this example, Active Record is providing an object-oriented interface for accessing data stored in the database. But Active Record provides much more functionality than this.
With Active Record, instead of writing raw SQL statements to perform database queries, you can call intuitive methods to achieve the same goals. For example, calling [[ActiveRecord::save()|save()]] would perform an INSERT or UPDATE query, creating or updating a row in the associated table of the ActiveRecord class:
```php
$customer = new Customer();
@ -24,7 +22,7 @@ Declaring ActiveRecord Classes
------------------------------
To declare an ActiveRecord class you need to extend [[\yii\db\ActiveRecord]] and
implement the `tableName` method like the following:
implement the `tableName` method:
```php
use yii\db\ActiveRecord;
@ -41,13 +39,19 @@ class Customer extends ActiveRecord
}
```
Connecting to Database
The `tableName` method only has to return the name of the database table associated with the class.
Class instances are obtained in one of two ways:
* Using the `new` operator to create a new, empty object
* Using a method to fetch an existing record (or records) from the database
Connecting to the Database
----------------------
ActiveRecord relies on a [[Connection|DB connection]] to perform the underlying DB operations.
By default, it assumes that there is an application component named `db` which gives the needed
[[Connection]] instance. Usually this component is configured via application configuration
like the following:
By default, ActiveRecord assumes that there is an application component named `db` which provides the needed
[[Connection]] instance. Usually this component is configured in application configuration file:
```php
return array(
@ -62,16 +66,9 @@ return array(
);
```
Please read the [Database basics](database-basics.md) section to learn more on how to configure
and use database connections.
> Tip: To use a different database connection, you may override the [[ActiveRecord::getDb()]] method.
You may create a base ActiveRecord class and override its [[ActiveRecord::getDb()]] method. You
then extend from this base class for all those ActiveRecord classes that need to use the same
DB connection.
Please read the [Database basics](database-basics.md) section to learn more on how to configure and use database connections.
Querying Data from Database
Querying Data from the Database
---------------------------
There are two ActiveRecord methods for querying data from database:
@ -79,8 +76,8 @@ There are two ActiveRecord methods for querying data from database:
- [[ActiveRecord::find()]]
- [[ActiveRecord::findBySql()]]
They both return an [[ActiveQuery]] instance which extends from [[Query]] and thus supports
the same set of flexible and powerful DB query methods. The followings are some examples,
Both methods return an [[ActiveQuery]] instance, which extends [[Query]], and thus supports
the same set of flexible and powerful DB query methods. The following examples demonstrate some of the possibilities.
```php
// to retrieve all *active* customers and order them by their ID:
@ -121,27 +118,26 @@ $customers = Customer::find()->indexBy('id')->all();
Accessing Column Data
---------------------
ActiveRecord maps each column of the corresponding database table row to an *attribute* in the ActiveRecord
object. An attribute is like a regular object property whose name is the same as the corresponding column
name and is case-sensitive.
ActiveRecord maps each column of the corresponding database table row to an attribute in the ActiveRecord
object. The attribute behaves like any regular object public property. The attribute's name will be the same as the corresponding column
name, and is case-sensitive.
To read the value of a column, you can use the following expression:
To read the value of a column, you can use the following syntax:
```php
// "id" is the name of a column in the table associated with $customer ActiveRecord object
// "id" and "email" are the names of columns in the table associated with $customer ActiveRecord object
$id = $customer->id;
// or alternatively,
$id = $customer->getAttribute('id');
$email = $customer->email;
```
You can get all column values through the [[ActiveRecord::attributes]] property:
To change the value of a column, assign a new value to the associated property and save the object:
```php
$values = $customer->attributes;
```
$customer->email = 'jane@example.com';
$customer->save();
```
Manipulating Data in Database
Manipulating Data in the Database
-----------------------------
ActiveRecord provides the following methods to insert, update and delete data in the database:
@ -156,10 +152,8 @@ ActiveRecord provides the following methods to insert, update and delete data in
- [[ActiveRecord::deleteAll()|deleteAll()]]
Note that [[ActiveRecord::updateAll()|updateAll()]], [[ActiveRecord::updateAllCounters()|updateAllCounters()]]
and [[ActiveRecord::deleteAll()|deleteAll()]] are static methods and apply to the whole database
table, while the rest of the methods only apply to the row associated with the ActiveRecord object.
The followings are some examples:
and [[ActiveRecord::deleteAll()|deleteAll()]] are static methods that apply to the whole database
table. The other methods only apply to the row associated with the ActiveRecord object through which the method is being called.
```php
// to insert a new customer record
@ -181,12 +175,21 @@ $customer->delete();
Customer::updateAllCounters(array('age' => 1));
```
Notice that you can always use the `save` method, and ActiveRecord will automatically perform an INSERT for new records and an UPDATE for existing ones.
Data Input and Validation
-------------------------
ActiveRecord inherits data validation and data input features from [[\yii\base\Model]]. Data validation is called
automatically when `save()` is performed. If data validation fails, the saving operation will be cancelled.
For more details refer to the [Model](model.md) section of this guide.
Querying Relational Data
------------------------
You can use ActiveRecord to query the relational data of a table. The relational data returned can
be accessed like a property of the ActiveRecord object associated with the primary table.
You can use ActiveRecord to also query a table's relational data (i.e., selection of data from Table A can also pull in related data from Table B). Thanks to ActiveRecord, the relational data returned can be accessed like a property of the ActiveRecord object associated with the primary table.
For example, with an appropriate relation declaration, by accessing `$customer->orders` you may obtain
an array of `Order` objects which represent the orders placed by the specified customer.
@ -405,15 +408,6 @@ The [[link()]] call above will set the `customer_id` of the order to be the prim
value of `$customer` and then call [[save()]] to save the order into database.
Data Input and Validation
-------------------------
ActiveRecord inherits data validation and data input features from [[\yii\base\Model]]. Data validation is called
automatically when `save()` is performed. If data validation fails, the saving operation will be cancelled.
For more details refer to the [Model](model.md) section of this guide.
Life Cycles of an ActiveRecord Object
-------------------------------------

11
docs/guide/bootstrap-widgets.md

@ -1,18 +1,17 @@
Bootstrap widgets
=================
Yii includes support of [Bootstrap 3](http://getbootstrap.com/) markup and components framework out of the box. It is an
excellent framework that allows you to speed up development a lot.
Out of the box, Yii includes support for the [Bootstrap 3](http://getbootstrap.com/) markup and components framework (also known as "Twitter Bootstrap"). Bootstrap is an excellent, responsive framework that can greatly speed up the client-side of your development process.
Bootstrap is generally about two parts:
The core of Bootstrap is represented by two parts:
- Basics such as grid system, typography, helper classes and responsive utilities.
- Ready to use components such as menus, pagination, modal boxes, tabs etc.
- CSS basics, such as a grid layout system, typography, helper classes, and responsive utilities.
- Ready to use components, such as menus, pagination, modal boxes, tabs etc.
Basics
------
Yii doesn't wrap bootstrap basics into PHP code since HTML is very simple by itself in this case. You can find details
Yii doesn't wrap the bootstrap basics into PHP code since HTML is very simple by itself in this case. You can find details
about using the basics at [bootstrap documentation website](http://getbootstrap.com/css/). Still Yii provides a
convenient way to include bootstrap assets in your pages with a single line added to `AppAsset.php` located in your
`config` directory:

17
docs/guide/database-basics.md

@ -2,8 +2,14 @@ Database basics
===============
Yii has a database access layer built on top of PHP's [PDO](http://www.php.net/manual/en/ref.pdo.php). It provides
uniform API and solves some inconsistencies between different DBMS. By default Yii supports MySQL, SQLite, PostgreSQL,
Oracle and MSSQL.
uniform API and solves some inconsistencies between different DBMS. By default Yii supports the following DBMS:
- [MySQL](http://www.mysql.com/)
- [SQLite](http://sqlite.org/)
- [PostgreSQL](http://www.postgresql.org/)
- [CUBRID](http://www.cubrid.org/) (version 9.1.0 and higher).
- Oracle
- MSSQL
Configuration
@ -22,6 +28,7 @@ return array(
'dsn' => 'mysql:host=localhost;dbname=mydatabase', // MySQL, MariaDB
//'dsn' => 'sqlite:/path/to/database/file', // SQLite
//'dsn' => 'pgsql:host=localhost;port=5432;dbname=mydatabase', // PostgreSQL
//'dsn' => 'cubrid:dbname=demodb;host=localhost;port=33000', // CUBRID
//'dsn' => 'sqlsrv:Server=localhost;Database=mydatabase', // MS SQL Server, sqlsrv driver
//'dsn' => 'dblib:host=localhost;dbname=mydatabase', // MS SQL Server, dblib driver
//'dsn' => 'mssql:host=localhost;dbname=mydatabase', // MS SQL Server, mssql driver
@ -34,8 +41,10 @@ return array(
// ...
);
```
Please refer to the [PHP manual](http://www.php.net/manual/en/function.PDO-construct.php) for more details
on the format of the DSN string.
After the component is configured you can access it using the following syntax:
After the connection component is configured you can access it using the following syntax:
```php
$connection = \Yii::$app->db;
@ -79,7 +88,7 @@ When only a single row is returned:
```php
$command = $connection->createCommand('SELECT * FROM tbl_post WHERE id=1');
$post = $command->query();
$post = $command->queryOne();
```
When there are multiple values from the same column:

14
docs/guide/model.md

@ -1,15 +1,15 @@
Model
=====
A model in Yii is intended for application data storage and has the following basic features:
In keeping with the MVC approach, a model in Yii is intended for storing or temporarily representing application data. Yii models have the following basic features:
- attribute declaration: a model defines what is considered an attribute.
- attribute labels: each attribute may be associated with a label for display purpose.
- massive attribute assignment.
- scenario-based data validation.
- Attribute declaration: a model defines what is considered an attribute.
- Attribute labels: each attribute may be associated with a label for display purpose.
- Massive attribute assignment: the ability to populate multiple model attributes in one step.
- Scenario-based data validation.
Models extending from [[\yii\base\Model]] class are typically used to hold data and corresponding validation rules of complex web forms.
The class is also a base for more advanced models with additional functionality such as [Active Record](active-record.md).
Models in Yii extend from the [[\yii\base\Model]] class. Models are typically used to both hold data and define the validation rules for that data. The validation rules greatly simply the generation of models from complex web forms.
The Model class is also the base for more advanced models with additional functionality such as [Active Record](active-record.md).
Attributes
----------

20
docs/guide/overview.md

@ -1,8 +1,7 @@
What is Yii
===========
Yii is a high-performance, component-based PHP framework for developing
large-scale Web applications rapidly. It enables maximum reusability in Web
Yii is a high-performance, component-based PHP framework for rapidly developing large-scale Web applications. Yii enables maximum reusability in Web
programming and can significantly accelerate your Web application development
process. The name Yii (pronounced `Yee` or `[ji:]`) is an acronym for
**Yes It Is!**.
@ -12,7 +11,7 @@ Requirements
------------
To run a Yii-powered Web application, you need a Web server that supports
PHP 5.3.?.
PHP 5.3.? or greater.
For developers who want to use Yii, understanding object-oriented
programming (OOP) is very helpful, because Yii is a pure OOP framework.
@ -25,16 +24,15 @@ Yii is a generic Web programming framework that can be used for developing
virtually any type of Web application. Because it is light-weight and
equipped with sophisticated caching mechanisms, it is especially suited
to high-traffic applications, such as portals, forums, content
management systems (CMS), e-commerce systems, etc.
management systems (CMS), e-commerce projects, etc.
How does Yii Compare with Other Frameworks?
-------------------------------------------
- Like most PHP frameworks, Yii is an MVC (Model-View-Controller) framework.
- It is a fullstack framework providing many solutions and components such as logging, session management, caching etc.
- It has a good balance of simplicity and features.
- Syntax and overall development usability are taken seriously.
- Performance is one of the key goals.
- We are constantly watching other web frameworks out there and getting the best ideas in. Initial Yii release was heavily
influenced by Ruby on Rails. Still, we aren't blindly copying anything.
- Like most PHP frameworks, Yii is uses the MVC (Model-View-Controller) design approach.
- Yii is a fullstack framework providing many solutions and components, such as logging, session management, caching etc.
- Yii strikes a good balance between simplicity and features.
- Syntax and overall development usability are taken seriously by the Yii development team.
- Performance is one of the key goals for the Yii framework.
- The Yii development team is constantly watching what other Web frameworks are doing to see what best practices and features should be incorporated into Yii. The initial Yii release was heavily influenced by Ruby on Rails. Still, no framework or feature is being blindly copied into Yii; all decisions are based upon what's best for Web developers and in keeping with Yii's philosophy.

7
docs/guide/security.md

@ -4,12 +4,9 @@ Security
Hashing and verifying passwords
------------------------------
It is important not to store passwords in plain text but, contrary to popular belief, just using `md5` or `sha1` to
compute and verify hashes isn't a good way either. Modern hardware allows to brute force these very fast.
Most developers know that you cannot store passwords in plain text, but many believe it's safe to hash passwords using `md5` or `sha1`. There was a time when those hashing algorithms were sufficient, but modern hardware makes it possible to break those hashes very quickly using a brute force attack.
In order to truly secure user passwords even in case your database is leaked you need to use a function that is resistant
to brute-force such as bcrypt. In PHP it can be achieved by using [crypt function](http://php.net/manual/en/function.crypt.php)
but since usage isn't trivial and one can easily misuse it, Yii provides two helper functions for generating hash from
In order to truly secure user passwords, even in the worst case scenario (your database is broken into), you need to use a hashing algorithm that is resistant to brute force attacks. The best current choice is bcrypt. In PHP, you can create a bcrypt hash by using [crypt function](http://php.net/manual/en/function.crypt.php). However, this function is not easy to use properly, so Yii provides two helper functions for generating hash from
password and verifying existing hash.
When user sets his password we're taking password string from POST and then getting a hash:

39
docs/guide/template.md

@ -1,11 +1,10 @@
Using template engines
======================
By default Yii uses PHP as template language but you can configure it to be able
to render templates with special engines such as Twig or Smarty.
By default Yii uses PHP as template language, but you can configure it to support other rendering engines, such as [Twig](http://twig.sensiolabs.org/) or [Smarty](http://www.smarty.net/).
The component responsible for rendering a view is called `view`. You can add
a custom template engines as follows:
The `view` component is responsible for rendering views. You can add
a custom template engines by reconfiguring this component's behavior:
```php
array(
@ -27,15 +26,13 @@ array(
)
```
Note that Smarty and Twig are not bundled with Yii and you have to download and
unpack these yourself and then specify `twigPath` and `smartyPath` respectively.
Note that the Smarty and Twig packages themselves are not bundled with Yii. You must download them yourself. Then unpack the packages and place the resulting files in a logical location, such as the application's `protected/vendor` folder. Finally, specify the correct `smartyPath` or `twigPath`, as in the code above (for Twig).
Twig
----
In order to use Twig you need to put you templates in files with extension `.twig`
(or another one if configured differently).
Also you need to specify this extension explicitly when calling `$this->render()`
To use Twig, you need to create templates in files with the `.twig` extension (or use another file extension but configure the component accordingly).
Unlike standard view files, when using Twig, you must include the extension when calling `$this->render()`
or `$this->renderPartial()` from your controller:
```php
@ -44,25 +41,25 @@ echo $this->render('renderer.twig', array('username' => 'Alex'));
### Additional functions
Additionally to regular Twig syntax the following is available in Yii:
Yii adds the following construct to the standard Twig syntax:
```php
<a href="{{ path('blog/view', {'alias' : post.alias}) }}">{{ post.title }}</a>
```
path function calls `Html::url()` internally.
Internally, the `path()` function calls Yii's `Html::url()` method.
### Additional variables
- `app` = `\Yii::$app`
- `this` = current `View` object
Within Twig templates, you can also make use of these variables:
- `app`, which equates to `\Yii::$app`
- `this`, which equates to the current `View` object
Smarty
------
In order to use Smarty you need to put you templates in files with extension `.tpl`
(or another one if configured differently).
Also you need to specify this extension explicitly when calling `$this->render()`
To use Smarty, you need to create templates in files with the `.tpl` extension (or use another file extension but configure the component accordingly). Unlike standard view files, when using Smarty, you must include the extension when calling `$this->render()`
or `$this->renderPartial()` from your controller:
```php
@ -71,16 +68,18 @@ echo $this->render('renderer.tpl', array('username' => 'Alex'));
### Additional functions
Additionally to regular Smarty syntax the following is available in Yii:
Yii adds the following construct to the standard Smarty syntax:
```php
<a href="{path route='blog/view' alias=$post.alias}">{$post.title}</a>
```
path function calls `Html::url()` internally.
Internally, the `path()` function calls Yii's `Html::url()` method.
### Additional variables
- `$app` = `\Yii::$app`
- `$this` = current `View` object
Within Smarty templates, you can also make use of these variables:
- `$app`, which equates to `\Yii::$app`
- `$this`, which equates to the current `View` object

6
docs/guide/upgrade-from-v1.md

@ -450,11 +450,11 @@ This feature is especially useful if you are developing an application that supp
different DBMS.
User and Identity
-----------------
User and IdentityInterface
--------------------------
The `CWebUser` class in 1.1 is now replaced by `\yii\Web\User`, and there is no more
`CUserIdentity` class. Instead, you should implement the `Identity` interface which
`CUserIdentity` class. Instead, you should implement the `IdentityInterface` which
is much more straightforward to implement. The bootstrap application provides such an example.

188
docs/guide/validation.md

@ -7,22 +7,176 @@ In order to learn model validation basics please refer to [Model, Validation sub
Standard Yii validators
-----------------------
- `boolean`: [[BooleanValidator]]
- `captcha`: [[CaptchaValidator]]
- `compare`: [[CompareValidator]]
- `date`: [[DateValidator]]
- `default`: [[DefaultValueValidator]]
- `double`: [[NumberValidator]]
- `email`: [[EmailValidator]]
- `exist`: [[ExistValidator]]
- `file`: [[FileValidator]]
- `filter`: [[FilterValidator]]
- `in`: [[RangeValidator]]
- `integer`: [[NumberValidator]]
- `match`: [[RegularExpressionValidator]]
- `required`: [[RequiredValidator]]
- `string`: [[StringValidator]]
- `unique`: [[UniqueValidator]]
- `url`: [[UrlValidator]]
Standard Yii validators could be specified using aliases instead of referring to class names. Here's the list of all
validators budled with Yii with their most useful properties:
### `boolean`: [[BooleanValidator]]
Checks if the attribute value is a boolean value.
- `trueValue`, the value representing true status. _(1)_
- `falseValue`, the value representing false status. _(0)_
- `strict`, whether to compare the type of the value and `trueValue`/`falseValue`. _(false)_
### `captcha`: [[CaptchaValidator]]
Validates that the attribute value is the same as the verification code displayed in the CAPTCHA. Should be used together
with [[CaptchaAction]].
- `caseSensitive` whether the comparison is case sensitive. _(false)_
- `captchaAction` the route of the controller action that renders the CAPTCHA image. _('site/captcha')_
### `compare`: [[CompareValidator]]
Compares the specified attribute value with another value and validates if they are equal.
- `compareAttribute` the name of the attribute to be compared with. _(currentAttribute_repeat)_
- `compareValue` the constant value to be compared with.
- `operator` the operator for comparison. _('==')_
### `date`: [[DateValidator]]
Verifies if the attribute represents a date, time or datetime in a proper format.
- `format` the date format that the value being validated should follow accodring to [[http://www.php.net/manual/en/datetime.createfromformat.php]]. _('Y-m-d')_
- `timestampAttribute` the name of the attribute to receive the parsing result.
### `default`: [[DefaultValueValidator]]
Sets the attribute to be the specified default value.
- `value` the default value to be set to the specified attributes.
### `double`: [[NumberValidator]]
Validates that the attribute value is a number.
- `max` limit of the number. _(null)_
- `min` lower limit of the number. _(null)_
### `email`: [[EmailValidator]]
Validates that the attribute value is a valid email address.
- `allowName` whether to allow name in the email address (e.g. `John Smith <john.smith@example.com>`). _(false)_.
- `checkMX` whether to check the MX record for the email address. _(false)_
- `checkPort` whether to check port 25 for the email address. _(false)_
- `enableIDN` whether validation process should take into account IDN (internationalized domain names). _(false)_
### `exist`: [[ExistValidator]]
Validates that the attribute value exists in a table.
- `className` the ActiveRecord class name or alias of the class that should be used to look for the attribute value being
validated. _(ActiveRecord class of the attribute being validated)_
- `attributeName` the ActiveRecord attribute name that should be used to look for the attribute value being validated.
_(name of the attribute being validated)_
### `file`: [[FileValidator]]
Verifies if an attribute is receiving a valid uploaded file.
- `types` a list of file name extensions that are allowed to be uploaded. _(any)_
- `minSize` the minimum number of bytes required for the uploaded file.
- `maxSize` the maximum number of bytes required for the uploaded file.
- `maxFiles` the maximum file count the given attribute can hold. _(1)_
### `filter`: [[FilterValidator]]
Converts the attribute value according to a filter.
- `filter` PHP callback that defines a filter.
Typically a callback is either the name of PHP function:
```php
array('password', 'filter', 'filter' => 'trim'),
```
Or an anonymous function:
```php
array('text', 'filter', 'filter' => function ($value) {
// here we are removing all swear words from text
return $newValue;
}),
```
### `in`: [[RangeValidator]]
Validates that the attribute value is among a list of values.
- `range` list of valid values that the attribute value should be among.
- `strict` whether the comparison is strict (both type and value must be the same). _(false)_
- `not` whether to invert the validation logic. _(false)_
### `integer`: [[NumberValidator]]
Validates that the attribute value is an integer number.
- `max` limit of the number. _(null)_
- `min` lower limit of the number. _(null)_
### `match`: [[RegularExpressionValidator]]
Validates that the attribute value matches the specified pattern defined by regular expression.
- `pattern` the regular expression to be matched with.
- `not` whether to invert the validation logic. _(false)_
### `required`: [[RequiredValidator]]
Validates that the specified attribute does not have null or empty value.
- `requiredValue` the desired value that the attribute must have. _(any)_
- `strict` whether the comparison between the attribute value and [[requiredValue]] is strict. _(false)_
### `safe`: [[SafeValidator]]
Serves as a dummy validator whose main purpose is to mark the attributes to be safe for massive assignment.
### `string`: [[StringValidator]]
Validates that the attribute value is of certain length.
- `length` specifies the length limit of the value to be validated. Can be `exactly X`, `array(min X)`, `array(min X, max Y)`.
- `max` maximum length. If not set, it means no maximum length limit.
- `min` minimum length. If not set, it means no minimum length limit.
- `encoding` the encoding of the string value to be validated. _([[\yii\base\Application::charset]])_
### `unique`: [[UniqueValidator]]
Validates that the attribute value is unique in the corresponding database table.
- `className` the ActiveRecord class name or alias of the class that should be used to look for the attribute value being
validated. _(ActiveRecord class of the attribute being validated)_
- `attributeName` the ActiveRecord attribute name that should be used to look for the attribute value being validated.
_(name of the attribute being validated)_
### `url`: [[UrlValidator]]
Validates that the attribute value is a valid http or https URL.
- `validSchemes` list of URI schemes which should be considered valid. _array('http', 'https')_
- `defaultScheme` the default URI scheme. If the input doesn't contain the scheme part, the default scheme will be
prepended to it. _(null)_
- `enableIDN` whether validation process should take into account IDN (internationalized domain names). _(false)_
Validating values out of model context
--------------------------------------
Sometimes you need to validate a value that is not bound to any model such as email. In Yii `Validator` class has
`validateValue` method that can help you with it. Not all validator classes have it implemented but the ones that can
operate without model do. In our case to validate an email we can do the following:
```php
$email = 'test@example.com';
$validator = new yii\validators\EmailValidator();
if ($validator->validateValue($email)) {
echo 'Email is valid.';
} else {
echo 'Email is not valid.'
}
```
TBD: refer to http://www.yiiframework.com/wiki/56/ for the format

2
docs/internals/autoloader.md

@ -16,4 +16,4 @@ PEAR-style libraries
References
----------
- YiiBase::autoload
- BaseYii::autoload

10
extensions/composer/README.md

@ -17,11 +17,11 @@ This is the yii2 composer installer.
Installation
------------
This extension offers you enhanced composer handling for your yii2-project. It will therefor require you to use composer.
This extension offers you enhanced Composer handling for your yii2-project. It will therefore require you to use Composer.
`
```
php composer.phar require yiisoft/yii2-composer "*"
`
```
*Note: You might have to run `php composer.phar selfupdate` before using this extension.*
@ -29,9 +29,9 @@ php composer.phar require yiisoft/yii2-composer "*"
Usage & Documentation
---------------------
This extensions allows you to hook to certain composer events and prepare your yii2-app for usage.
This extension allows you to hook to certain composer events and automate preparing your Yii2 application for further usage.
After the package is installed, the composer.json file has to be modified to enable this extension.
After the package is installed, the `composer.json` file has to be modified to enable this extension.
To see it in action take a look at the example apps in the repository:

10
framework/yii/YiiBase.php → framework/yii/BaseYii.php

@ -49,15 +49,15 @@ defined('YII_ENABLE_ERROR_HANDLER') or define('YII_ENABLE_ERROR_HANDLER', true);
/**
* YiiBase is the core helper class for the Yii framework.
* BaseYii is the core helper class for the Yii framework.
*
* Do not use YiiBase directly. Instead, use its child class [[Yii]] where
* you can customize methods of YiiBase.
* Do not use BaseYii directly. Instead, use its child class [[Yii]] where
* you can customize methods of BaseYii.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class YiiBase
class BaseYii
{
/**
* @var array class map used by the Yii autoloading mechanism.
@ -335,7 +335,7 @@ class YiiBase
include($classFile);
if (!class_exists($className, false) && !interface_exists($className, false) &&
if (YII_DEBUG && !class_exists($className, false) && !interface_exists($className, false) &&
(!function_exists('trait_exists') || !trait_exists($className, false))) {
throw new UnknownClassException("Unable to find '$className' in file: $classFile");
}

8
framework/yii/Yii.php

@ -7,18 +7,18 @@
* @license http://www.yiiframework.com/license/
*/
require(__DIR__ . '/YiiBase.php');
require(__DIR__ . '/BaseYii.php');
/**
* Yii is a helper class serving common framework functionalities.
*
* It extends from [[YiiBase]] which provides the actual implementation.
* By writing your own Yii class, you can customize some functionalities of [[YiiBase]].
* It extends from [[BaseYii]] which provides the actual implementation.
* By writing your own Yii class, you can customize some functionalities of [[BaseYii]].
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Yii extends \yii\YiiBase
class Yii extends \yii\BaseYii
{
}

148
framework/yii/assets/yii.js

@ -37,13 +37,116 @@
*
* Using this structure, you can define public and private functions/properties for a module.
* Private functions/properties are only visible within the module, while public functions/properties
* may be accessed outside of the module. For example, you can access "yii.sample.init()".
* may be accessed outside of the module. For example, you can access "yii.sample.isActive".
*
* You must call "yii.initModule()" once for the root module of all your modules.
*/
yii = (function ($) {
var pub = {
version: '2.0',
/**
* The selector for clickable elements that need to support confirmation and form submission.
*/
clickableSelector: 'a, button, input[type="submit"], input[type="button"], input[type="reset"], input[type="image"]',
/**
* The selector for changeable elements that need to support confirmation and form submission.
*/
changeableSelector: 'select, input, textarea',
/**
* @return string|undefined the CSRF variable name. Undefined is returned is CSRF validation is not enabled.
*/
getCsrfVar: function () {
return $('meta[name=csrf-var]').prop('content');
},
/**
* @return string|undefined the CSRF token. Undefined is returned is CSRF validation is not enabled.
*/
getCsrfToken: function () {
return $('meta[name=csrf-token]').prop('content');
},
/**
* Displays a confirmation dialog.
* The default implementation simply displays a js confirmation dialog.
* You may override this by setting `yii.confirm`.
* @param message the confirmation message.
* @return boolean whether the user confirms with the message in the dialog
*/
confirm: function (message) {
return confirm(message);
},
/**
* Returns a value indicating whether to allow executing the action defined for the specified element.
* This method recognizes the `data-confirm` attribute of the element and uses it
* as the message in a confirmation dialog. The method will return true if this special attribute
* is not defined or if the user confirms the message.
* @param $e the jQuery representation of the element
* @return boolean whether to allow executing the action defined for the specified element.
*/
allowAction: function ($e) {
var message = $e.data('confirm');
return message === undefined || pub.confirm(message);
},
/**
* Handles the action triggered by user.
* This method recognizes the `data-method` attribute of the element. If the attribute exists,
* the method will submit the form containing this element. If there is no containing form, a form
* will be created and submitted using the method given by this attribute value (e.g. "post", "put").
* For hyperlinks, the form action will take the value of the "href" attribute of the link.
* For other elements, either the containing form action or the current page URL will be used
* as the form action URL.
*
* If the `data-method` attribute is not defined, the default element action will be performed.
*
* @param $e the jQuery representation of the element
* @return boolean whether to execute the default action for the element.
*/
handleAction: function ($e) {
var method = $e.data('method');
if (method === undefined) {
return true;
}
var $form = $e.closest('form');
var newForm = !$form.length;
if (newForm) {
var action = $e.prop('href');
if (!action || !action.match(/(^\/|:\/\/)/)) {
action = window.location.href;
}
$form = $('<form method="' + method + '" action="' + action + '"></form>');
var target = $e.prop('target');
if (target) {
$form.attr('target', target);
}
if (!method.match(/(get|post)/i)) {
$form.append('<input name="_method" value="' + method + '" type="hidden">');
}
var csrfVar = pub.getCsrfVar();
if (csrfVar) {
$form.append('<input name="' + csrfVar + '" value="' + pub.getCsrfToken() + '" type="hidden">');
}
$form.hide().appendTo('body');
}
var activeFormData = $form.data('yiiActiveForm');
if (activeFormData) {
// remember who triggers the form submission. This is used by yii.activeForm.js
activeFormData.submitObject = $e;
}
$form.trigger('submit');
if (newForm) {
$form.remove();
}
return false;
},
initModule: function (module) {
if (module.isActive === undefined || module.isActive) {
if ($.isFunction(module.init)) {
@ -55,6 +158,47 @@ yii = (function ($) {
}
});
}
},
init: function () {
var $document = $(document);
// automatically send CSRF token for all AJAX requests
$.ajaxPrefilter(function (options, originalOptions, xhr) {
if (!options.crossDomain && pub.getCsrfVar()) {
xhr.setRequestHeader('X-CSRF-Token', pub.getCsrfToken());
}
});
// handle AJAX redirection
$document.ajaxComplete(function (event, xhr, settings) {
var url = xhr.getResponseHeader('X-Redirect');
if (url) {
window.location = url;
}
});
// handle data-confirm and data-method for clickable elements
$document.on('click.yii', pub.clickableSelector, function (event) {
var $this = $(this);
if (pub.allowAction($this)) {
return pub.handleAction($this);
} else {
event.stopImmediatePropagation();
return false;
}
});
// handle data-confirm and data-method for changeable elements
$document.on('change.yii', pub.changeableSelector, function (event) {
var $this = $(this);
if (pub.allowAction($this)) {
return pub.handleAction($this);
} else {
event.stopImmediatePropagation();
return false;
}
});
}
};
return pub;

14
framework/yii/base/Component.php

@ -40,6 +40,7 @@ class Component extends Object
* @param string $name the property name
* @return mixed the property value or the value of a behavior's property
* @throws UnknownPropertyException if the property is not defined
* @throws InvalidCallException if the property is write-only.
* @see __set
*/
public function __get($name)
@ -178,9 +179,8 @@ class Component extends Object
/**
* Calls the named method which is not a class method.
* If the name refers to a component property whose value is
* an anonymous function, the method will execute the function.
* Otherwise, it will check if any attached behavior has
*
* This method will check if any attached behavior has
* the named method and will execute it if available.
*
* Do not call this method directly as it is a PHP magic method that
@ -192,14 +192,6 @@ class Component extends Object
*/
public function __call($name, $params)
{
$getter = 'get' . $name;
if (method_exists($this, $getter)) {
$func = $this->$getter();
if ($func instanceof \Closure) {
return call_user_func_array($func, $params);
}
}
$this->ensureBehaviors();
foreach ($this->_behaviors as $object) {
if ($object->hasMethod($name)) {

2
framework/yii/base/Controller.php

@ -210,6 +210,7 @@ class Controller extends Component
/**
* This method is invoked right before an action is to be executed (after all possible filters.)
* You may override this method to do last-minute preparation for the action.
* If you override this method, please make sure you call the parent implementation first.
* @param Action $action the action to be executed.
* @return boolean whether the action should continue to be executed.
*/
@ -223,6 +224,7 @@ class Controller extends Component
/**
* This method is invoked right after an action is executed.
* You may override this method to do some postprocessing for the action.
* If you override this method, please make sure you call the parent implementation first.
* @param Action $action the action just executed.
* @param mixed $result the action return result.
*/

2
framework/yii/base/ErrorHandler.php

@ -93,6 +93,8 @@ class ErrorHandler extends Component
$response->getHeaders()->removeAll();
if ($useErrorView && $this->errorAction !== null) {
// disable CSRF validation so that errorAction can run in case the error is caused by CSRF validation failure
Yii::$app->getRequest()->enableCsrfValidation = false;
$result = Yii::$app->runAction($this->errorAction);
if ($result instanceof Response) {
$response = $result;

21
framework/yii/base/Exception.php

@ -39,21 +39,12 @@ class Exception extends \Exception implements Arrayable
*/
protected function toArrayRecursive($exception)
{
if ($exception instanceof self) {
$array = array(
'type' => get_class($this),
'name' => $this->getName(),
'message' => $this->getMessage(),
'code' => $this->getCode(),
);
} else {
$array = array(
'type' => get_class($exception),
'name' => 'Exception',
'message' => $exception->getMessage(),
'code' => $exception->getCode(),
);
}
$array = array(
'type' => get_class($exception),
'name' => $exception instanceof self ? $exception->getName() : 'Exception',
'message' => $exception->getMessage(),
'code' => $exception->getCode(),
);
if (($prev = $exception->getPrevious()) !== null) {
$array['previous'] = $this->toArrayRecursive($prev);
}

51
framework/yii/base/Model.php

@ -45,7 +45,7 @@ use yii\validators\Validator;
* property is read-only.
* @property ArrayIterator $iterator An iterator for traversing the items in the list. This property is
* read-only.
* @property string $scenario The scenario that this model is in. Defaults to 'default'.
* @property string $scenario The scenario that this model is in. Defaults to [[DEFAULT_SCENARIO]].
* @property ArrayObject $validators All the validators declared in the model. This property is read-only.
*
* @author Qiang Xue <qiang.xue@gmail.com>
@ -54,6 +54,11 @@ use yii\validators\Validator;
class Model extends Component implements IteratorAggregate, ArrayAccess
{
/**
* The name of the default scenario.
*/
const DEFAULT_SCENARIO = 'default';
/**
* @event ModelEvent an event raised at the beginning of [[validate()]]. You may set
* [[ModelEvent::isValid]] to be false to stop the validation.
*/
@ -74,7 +79,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
/**
* @var string current scenario
*/
private $_scenario = 'default';
private $_scenario = self::DEFAULT_SCENARIO;
/**
* Returns the validation rules for attributes.
@ -111,6 +116,10 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
* function validatorName($attribute, $params)
* ~~~
*
* In the above `$attribute` refers to currently validated attribute name while `$params` contains an array of
* validator configuration options such as `max` in case of `length` validator. Currently validate attribute value
* can be accessed as `$this->[$attribute]`.
*
* Yii also provides a set of [[Validator::builtInValidators|built-in validators]].
* They each has an alias name which can be used when specifying a validation rule.
*
@ -159,23 +168,39 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
* If an attribute should NOT be massively assigned (thus considered unsafe),
* please prefix the attribute with an exclamation character (e.g. '!rank').
*
* The default implementation of this method will return a 'default' scenario
* which corresponds to all attributes listed in the validation rules applicable
* to the 'default' scenario.
* The default implementation of this method will return all scenarios found in the [[rules()]]
* declaration. A special scenario named [[DEFAULT_SCENARIO]] will contain all attributes
* found in the [[rules()]]. Each scenario will be associated with the attributes that
* are being validated by the validation rules that apply to the scenario.
*
* @return array a list of scenarios and the corresponding active attributes.
*/
public function scenarios()
{
$attributes = array();
foreach ($this->getActiveValidators() as $validator) {
foreach ($validator->attributes as $name) {
$attributes[$name] = true;
$scenarios = array();
$defaults = array();
/** @var $validator Validator */
foreach ($this->getValidators() as $validator) {
if (empty($validator->on)) {
foreach ($validator->attributes as $attribute) {
$defaults[$attribute] = true;
}
} else {
foreach ($validator->on as $scenario) {
foreach ($validator->attributes as $attribute) {
$scenarios[$scenario][$attribute] = true;
}
}
}
}
foreach ($scenarios as $scenario => $attributes) {
foreach (array_keys($defaults) as $attribute) {
$attributes[$attribute] = true;
}
$scenarios[$scenario] = array_keys($attributes);
}
return array(
'default' => array_keys($attributes),
);
$scenarios[self::DEFAULT_SCENARIO] = array_keys($defaults);
return $scenarios;
}
/**
@ -593,7 +618,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
* Scenario affects how validation is performed and which attributes can
* be massively assigned.
*
* @return string the scenario that this model is in. Defaults to 'default'.
* @return string the scenario that this model is in. Defaults to [[DEFAULT_SCENARIO]].
*/
public function getScenario()
{

7
framework/yii/base/Module.php

@ -338,10 +338,9 @@ abstract class Module extends Component
/**
* Retrieves the named module.
* @param string $id module ID (case-sensitive)
* @param string $id module ID (case-sensitive).
* @param boolean $load whether to load the module if it is not yet loaded.
* @return Module|null the module instance, null if the module
* does not exist.
* @return Module|null the module instance, null if the module does not exist.
* @see hasModule()
*/
public function getModule($id, $load = true)
@ -618,7 +617,7 @@ abstract class Module extends Component
if (isset($this->controllerMap[$id])) {
$controller = Yii::createObject($this->controllerMap[$id], $id, $this);
} elseif (preg_match('/^[a-z0-9\\-_]+$/', $id)) {
$className = str_replace(' ', '', ucwords(implode(' ', explode('-', $id)))) . 'Controller';
$className = str_replace(' ', '', ucwords(str_replace('-', ' ', $id))) . 'Controller';
$classFile = $this->controllerPath . DIRECTORY_SEPARATOR . $className . '.php';
if (!is_file($classFile)) {
return false;

9
framework/yii/base/Object.php

@ -143,8 +143,6 @@ class Object implements Arrayable
/**
* Calls the named method which is not a class method.
* If the name refers to a component property whose value is
* an anonymous function, the method will execute the function.
*
* 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.
@ -155,13 +153,6 @@ class Object implements Arrayable
*/
public function __call($name, $params)
{
$getter = 'get' . $name;
if (method_exists($this, $getter)) {
$func = $this->$getter();
if ($func instanceof \Closure) {
return call_user_func_array($func, $params);
}
}
throw new UnknownMethodException('Unknown method: ' . get_class($this) . "::$name()");
}

7
framework/yii/base/View.php

@ -708,6 +708,13 @@ class View extends Component
if (!empty($this->metaTags)) {
$lines[] = implode("\n", $this->metaTags);
}
$request = Yii::$app->getRequest();
if ($request instanceof \yii\web\Request && $request->enableCsrfValidation) {
$lines[] = Html::tag('meta', '', array('name' => 'csrf-var', 'content' => $request->csrfVar));
$lines[] = Html::tag('meta', '', array('name' => 'csrf-token', 'content' => $request->getCsrfToken()));
}
if (!empty($this->linkTags)) {
$lines[] = implode("\n", $this->linkTags);
}

4
framework/yii/bootstrap/Nav.php

@ -56,7 +56,7 @@ class Nav extends Widget
{
/**
* @var array list of items in the nav widget. Each array element represents a single
* menu item with the following structure:
* menu item which can be either a string or an array with the following structure:
*
* - label: string, required, the nav item label.
* - url: optional, the item's URL. Defaults to "#".
@ -66,6 +66,8 @@ class Nav extends Widget
* - active: boolean, optional, whether the item should be on active state or not.
* - items: array|string, optional, the configuration array for creating a [[Dropdown]] widget,
* or a string representing the dropdown menu. Note that Bootstrap does not support sub-dropdown menus.
*
* If a menu item is a string, it will be rendered directly without HTML encoding.
*/
public $items = array();
/**

29
framework/yii/caching/MemCache.php

@ -87,7 +87,14 @@ class MemCache extends Cache
parent::init();
$servers = $this->getServers();
$cache = $this->getMemCache();
if (count($servers)) {
if (empty($servers)) {
$cache->addServer('127.0.0.1', 11211);
} else {
if (!$this->useMemcached) {
// different version of memcache may have different number of parameters for the addServer method.
$class = new \ReflectionClass($cache);
$paramCount = $class->getMethod('addServer')->getNumberOfParameters();
}
foreach ($servers as $server) {
if ($server->host === null) {
throw new InvalidConfigException("The 'host' property must be specified for every memcache server.");
@ -97,15 +104,21 @@ class MemCache extends Cache
} else {
// $timeout is used for memcache versions that do not have timeoutms parameter
$timeout = (int) ($server->timeout / 1000) + (($server->timeout % 1000 > 0) ? 1 : 0);
$cache->addServer(
$server->host, $server->port, $server->persistent,
$server->weight, $timeout, $server->retryInterval,
$server->status, $server->failureCallback, $server->timeout
);
if ($paramCount === 9) {
$cache->addServer(
$server->host, $server->port, $server->persistent,
$server->weight, $timeout, $server->retryInterval,
$server->status, $server->failureCallback, $server->timeout
);
} else {
$cache->addServer(
$server->host, $server->port, $server->persistent,
$server->weight, $timeout, $server->retryInterval,
$server->status, $server->failureCallback
);
}
}
}
} else {
$cache->addServer('127.0.0.1', 11211);
}
}

10
framework/yii/caching/RedisCache.php

@ -7,7 +7,7 @@
namespace yii\caching;
use yii\db\redis\Connection;
use yii\redis\Connection;
/**
* RedisCache implements a cache application component based on [redis](http://redis.io/).
@ -39,7 +39,7 @@ use yii\db\redis\Connection;
* )
* ~~~
*
* @property \yii\db\redis\Connection $connection This property is read-only.
* @property Connection $connection The redis connection object. This property is read-only.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
@ -71,7 +71,7 @@ class RedisCache extends Cache
*/
public $dataTimeout = null;
/**
* @var \yii\db\redis\Connection the redis connection
* @var Connection the redis connection
*/
private $_connection;
@ -88,9 +88,7 @@ class RedisCache extends Cache
/**
* Returns the redis connection object.
* Establishes a connection to the redis server if it does not already exists.
*
* TODO throw exception on error
* @return \yii\db\redis\Connection
* @return Connection the redis connection object.
*/
public function getConnection()
{

41
framework/yii/classes.php

@ -49,6 +49,7 @@ return array(
'yii\bootstrap\Alert' => YII_PATH . '/bootstrap/Alert.php',
'yii\bootstrap\BootstrapAsset' => YII_PATH . '/bootstrap/BootstrapAsset.php',
'yii\bootstrap\BootstrapPluginAsset' => YII_PATH . '/bootstrap/BootstrapPluginAsset.php',
'yii\bootstrap\BootstrapThemeAsset' => YII_PATH . '/bootstrap/BootstrapThemeAsset.php',
'yii\bootstrap\Button' => YII_PATH . '/bootstrap/Button.php',
'yii\bootstrap\ButtonDropdown' => YII_PATH . '/bootstrap/ButtonDropdown.php',
'yii\bootstrap\ButtonGroup' => YII_PATH . '/bootstrap/ButtonGroup.php',
@ -60,7 +61,6 @@ return array(
'yii\bootstrap\NavBar' => YII_PATH . '/bootstrap/NavBar.php',
'yii\bootstrap\Progress' => YII_PATH . '/bootstrap/Progress.php',
'yii\bootstrap\Tabs' => YII_PATH . '/bootstrap/Tabs.php',
'yii\bootstrap\Typeahead' => YII_PATH . '/bootstrap/Typeahead.php',
'yii\bootstrap\Widget' => YII_PATH . '/bootstrap/Widget.php',
'yii\caching\ApcCache' => YII_PATH . '/caching/ApcCache.php',
'yii\caching\Cache' => YII_PATH . '/caching/Cache.php',
@ -75,6 +75,7 @@ return array(
'yii\caching\GroupDependency' => YII_PATH . '/caching/GroupDependency.php',
'yii\caching\MemCache' => YII_PATH . '/caching/MemCache.php',
'yii\caching\MemCacheServer' => YII_PATH . '/caching/MemCacheServer.php',
'yii\caching\RedisCache' => YII_PATH . '/caching/RedisCache.php',
'yii\caching\WinCache' => YII_PATH . '/caching/WinCache.php',
'yii\caching\XCache' => YII_PATH . '/caching/XCache.php',
'yii\caching\ZendDataCache' => YII_PATH . '/caching/ZendDataCache.php',
@ -85,7 +86,7 @@ return array(
'yii\data\ActiveDataProvider' => YII_PATH . '/data/ActiveDataProvider.php',
'yii\data\ArrayDataProvider' => YII_PATH . '/data/ArrayDataProvider.php',
'yii\data\DataProvider' => YII_PATH . '/data/DataProvider.php',
'yii\data\IDataProvider' => YII_PATH . '/data/IDataProvider.php',
'yii\data\DataProviderInterface' => YII_PATH . '/data/DataProviderInterface.php',
'yii\data\Pagination' => YII_PATH . '/data/Pagination.php',
'yii\data\Sort' => YII_PATH . '/data/Sort.php',
'yii\db\ActiveQuery' => YII_PATH . '/db/ActiveQuery.php',
@ -104,10 +105,13 @@ return array(
'yii\db\StaleObjectException' => YII_PATH . '/db/StaleObjectException.php',
'yii\db\TableSchema' => YII_PATH . '/db/TableSchema.php',
'yii\db\Transaction' => YII_PATH . '/db/Transaction.php',
'yii\db\cubrid\QueryBuilder' => YII_PATH . '/db/cubrid/QueryBuilder.php',
'yii\db\cubrid\Schema' => YII_PATH . '/db/cubrid/Schema.php',
'yii\db\mssql\PDO' => YII_PATH . '/db/mssql/PDO.php',
'yii\db\mssql\QueryBuilder' => YII_PATH . '/db/mssql/QueryBuilder.php',
'yii\db\mssql\Schema' => YII_PATH . '/db/mssql/Schema.php',
'yii\db\mssql\SqlsrvPDO' => YII_PATH . '/db/mssql/SqlsrvPDO.php',
'yii\db\mssql\TableSchema' => YII_PATH . '/db/mssql/TableSchema.php',
'yii\db\mysql\QueryBuilder' => YII_PATH . '/db/mysql/QueryBuilder.php',
'yii\db\mysql\Schema' => YII_PATH . '/db/mysql/Schema.php',
'yii\db\pgsql\QueryBuilder' => YII_PATH . '/db/pgsql/QueryBuilder.php',
@ -121,27 +125,27 @@ return array(
'yii\grid\GridViewAsset' => YII_PATH . '/grid/GridViewAsset.php',
'yii\grid\SerialColumn' => YII_PATH . '/grid/SerialColumn.php',
'yii\helpers\ArrayHelper' => YII_PATH . '/helpers/ArrayHelper.php',
'yii\helpers\ArrayHelperBase' => YII_PATH . '/helpers/ArrayHelperBase.php',
'yii\helpers\BaseArrayHelper' => YII_PATH . '/helpers/BaseArrayHelper.php',
'yii\helpers\Console' => YII_PATH . '/helpers/Console.php',
'yii\helpers\ConsoleBase' => YII_PATH . '/helpers/ConsoleBase.php',
'yii\helpers\BaseConsole' => YII_PATH . '/helpers/BaseConsole.php',
'yii\helpers\FileHelper' => YII_PATH . '/helpers/FileHelper.php',
'yii\helpers\FileHelperBase' => YII_PATH . '/helpers/FileHelperBase.php',
'yii\helpers\BaseFileHelper' => YII_PATH . '/helpers/BaseFileHelper.php',
'yii\helpers\Html' => YII_PATH . '/helpers/Html.php',
'yii\helpers\HtmlBase' => YII_PATH . '/helpers/HtmlBase.php',
'yii\helpers\BaseHtml' => YII_PATH . '/helpers/BaseHtml.php',
'yii\helpers\HtmlPurifier' => YII_PATH . '/helpers/HtmlPurifier.php',
'yii\helpers\HtmlPurifierBase' => YII_PATH . '/helpers/HtmlPurifierBase.php',
'yii\helpers\BaseHtmlPurifier' => YII_PATH . '/helpers/BaseHtmlPurifier.php',
'yii\helpers\Inflector' => YII_PATH . '/helpers/Inflector.php',
'yii\helpers\InflectorBase' => YII_PATH . '/helpers/InflectorBase.php',
'yii\helpers\BaseInflector' => YII_PATH . '/helpers/BaseInflector.php',
'yii\helpers\Json' => YII_PATH . '/helpers/Json.php',
'yii\helpers\JsonBase' => YII_PATH . '/helpers/JsonBase.php',
'yii\helpers\BaseJson' => YII_PATH . '/helpers/BaseJson.php',
'yii\helpers\Markdown' => YII_PATH . '/helpers/Markdown.php',
'yii\helpers\MarkdownBase' => YII_PATH . '/helpers/MarkdownBase.php',
'yii\helpers\BaseMarkdown' => YII_PATH . '/helpers/BaseMarkdown.php',
'yii\helpers\Security' => YII_PATH . '/helpers/Security.php',
'yii\helpers\SecurityBase' => YII_PATH . '/helpers/SecurityBase.php',
'yii\helpers\BaseSecurity' => YII_PATH . '/helpers/BaseSecurity.php',
'yii\helpers\StringHelper' => YII_PATH . '/helpers/StringHelper.php',
'yii\helpers\StringHelperBase' => YII_PATH . '/helpers/StringHelperBase.php',
'yii\helpers\BaseStringHelper' => YII_PATH . '/helpers/BaseStringHelper.php',
'yii\helpers\VarDumper' => YII_PATH . '/helpers/VarDumper.php',
'yii\helpers\VarDumperBase' => YII_PATH . '/helpers/VarDumperBase.php',
'yii\helpers\BaseVarDumper' => YII_PATH . '/helpers/BaseVarDumper.php',
'yii\i18n\DbMessageSource' => YII_PATH . '/i18n/DbMessageSource.php',
'yii\i18n\Formatter' => YII_PATH . '/i18n/Formatter.php',
'yii\i18n\GettextFile' => YII_PATH . '/i18n/GettextFile.php',
@ -162,6 +166,8 @@ return array(
'yii\rbac\Item' => YII_PATH . '/rbac/Item.php',
'yii\rbac\Manager' => YII_PATH . '/rbac/Manager.php',
'yii\rbac\PhpManager' => YII_PATH . '/rbac/PhpManager.php',
'yii\redis\Connection' => YII_PATH . '/redis/Connection.php',
'yii\redis\Transaction' => YII_PATH . '/redis/Transaction.php',
'yii\requirements\YiiRequirementChecker' => YII_PATH . '/requirements/YiiRequirementChecker.php',
'yii\validators\BooleanValidator' => YII_PATH . '/validators/BooleanValidator.php',
'yii\validators\CompareValidator' => YII_PATH . '/validators/CompareValidator.php',
@ -177,6 +183,7 @@ return array(
'yii\validators\RangeValidator' => YII_PATH . '/validators/RangeValidator.php',
'yii\validators\RegularExpressionValidator' => YII_PATH . '/validators/RegularExpressionValidator.php',
'yii\validators\RequiredValidator' => YII_PATH . '/validators/RequiredValidator.php',
'yii\validators\SafeValidator' => YII_PATH . '/validators/SafeValidator.php',
'yii\validators\StringValidator' => YII_PATH . '/validators/StringValidator.php',
'yii\validators\UniqueValidator' => YII_PATH . '/validators/UniqueValidator.php',
'yii\validators\UrlValidator' => YII_PATH . '/validators/UrlValidator.php',
@ -197,15 +204,15 @@ return array(
'yii\web\HeaderCollection' => YII_PATH . '/web/HeaderCollection.php',
'yii\web\HttpCache' => YII_PATH . '/web/HttpCache.php',
'yii\web\HttpException' => YII_PATH . '/web/HttpException.php',
'yii\web\IAssetConverter' => YII_PATH . '/web/IAssetConverter.php',
'yii\web\Identity' => YII_PATH . '/web/Identity.php',
'yii\web\AssetConverterInterface' => YII_PATH . '/web/AssetConverterInterface.php',
'yii\web\IdentityInterface' => YII_PATH . '/web/IdentityInterface.php',
'yii\web\JqueryAsset' => YII_PATH . '/web/JqueryAsset.php',
'yii\web\JsExpression' => YII_PATH . '/web/JsExpression.php',
'yii\web\PageCache' => YII_PATH . '/web/PageCache.php',
'yii\web\Request' => YII_PATH . '/web/Request.php',
'yii\web\Response' => YII_PATH . '/web/Response.php',
'yii\web\ResponseEvent' => YII_PATH . '/web/ResponseEvent.php',
'yii\web\ResponseFormatter' => YII_PATH . '/web/ResponseFormatter.php',
'yii\web\ResponseFormatterInterface' => YII_PATH . '/web/ResponseFormatterInterface.php',
'yii\web\Session' => YII_PATH . '/web/Session.php',
'yii\web\SessionIterator' => YII_PATH . '/web/SessionIterator.php',
'yii\web\UploadedFile' => YII_PATH . '/web/UploadedFile.php',
@ -228,7 +235,7 @@ return array(
'yii\widgets\LinkPager' => YII_PATH . '/widgets/LinkPager.php',
'yii\widgets\LinkSorter' => YII_PATH . '/widgets/LinkSorter.php',
'yii\widgets\ListView' => YII_PATH . '/widgets/ListView.php',
'yii\widgets\ListViewBase' => YII_PATH . '/widgets/ListViewBase.php',
'yii\widgets\BaseListView' => YII_PATH . '/widgets/BaseListView.php',
'yii\widgets\MaskedInput' => YII_PATH . '/widgets/MaskedInput.php',
'yii\widgets\MaskedInputAsset' => YII_PATH . '/widgets/MaskedInputAsset.php',
'yii\widgets\Menu' => YII_PATH . '/widgets/Menu.php',

1
framework/yii/data/ActiveDataProvider.php

@ -156,7 +156,6 @@ class ActiveDataProvider extends DataProvider
throw new InvalidConfigException('The "query" property must be an instance of Query or its subclass.');
}
if (($pagination = $this->getPagination()) !== false) {
$pagination->totalCount = $this->getTotalCount();
$this->query->limit($pagination->getLimit())->offset($pagination->getOffset());
}
if (($sort = $this->getSort()) !== false) {

1
framework/yii/data/ArrayDataProvider.php

@ -114,7 +114,6 @@ class ArrayDataProvider extends DataProvider
}
if (($pagination = $this->getPagination()) !== false) {
$pagination->totalCount = $this->getTotalCount();
$models = array_slice($models, $pagination->getOffset(), $pagination->getLimit());
}

5
framework/yii/data/DataProvider.php

@ -14,7 +14,7 @@ use yii\base\InvalidParamException;
/**
* DataProvider is the base class of data provider classes.
*
* It implements the [[getPagination()]] and [[getSort()]] methods as specified by the [[IDataProvider]] interface.
* It implements the [[getPagination()]] and [[getSort()]] methods as specified by the [[DataProviderInterface]].
*
* @property integer $count The number of data models in the current page. This property is read-only.
* @property Pagination|boolean $pagination The pagination object. If this is false, it means the pagination
@ -26,7 +26,7 @@ use yii\base\InvalidParamException;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
abstract class DataProvider extends Component implements IDataProvider
abstract class DataProvider extends Component implements DataProviderInterface
{
/**
* @var string an ID that uniquely identifies the data provider among all data providers.
@ -48,6 +48,7 @@ abstract class DataProvider extends Component implements IDataProvider
if ($this->id !== null) {
$this->_pagination->pageVar = $this->id . '-page';
}
$this->_pagination->totalCount = $this->getTotalCount();
}
return $this->_pagination;
}

4
framework/yii/data/IDataProvider.php → framework/yii/data/DataProviderInterface.php

@ -8,7 +8,7 @@
namespace yii\data;
/**
* IDataProvider is the interface that must be implemented by data provider classes.
* DataProviderInterface is the interface that must be implemented by data provider classes.
*
* Data providers are components that sort and paginate data, and provide them to widgets
* such as [[GridView]], [[ListView]].
@ -16,7 +16,7 @@ namespace yii\data;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
interface IDataProvider
interface DataProviderInterface
{
/**
* Returns the number of data models in the current page.

4
framework/yii/data/Pagination.php

@ -98,10 +98,10 @@ class Pagination extends Object
*/
public $validatePage = true;
/**
* @var integer number of items on each page. Defaults to 10.
* @var integer number of items on each page. Defaults to 20.
* If it is less than 1, it means the page size is infinite, and thus a single page contains all items.
*/
public $pageSize = 10;
public $pageSize = 20;
/**
* @var integer total number of items.
*/

53
framework/yii/db/ActiveRecord.php

@ -749,21 +749,21 @@ class ActiveRecord extends Model
return false;
}
$db = static::getDb();
$transaction = $this->isTransactional(self::OP_INSERT) && $db->getTransaction() === null ? $db->beginTransaction() : null;
try {
$result = $this->insertInternal($attributes);
if ($transaction !== null) {
if ($this->isTransactional(self::OP_INSERT) && $db->getTransaction() === null) {
$transaction = $db->beginTransaction();
try {
$result = $this->insertInternal($attributes);
if ($result === false) {
$transaction->rollback();
} else {
$transaction->commit();
}
}
} catch (\Exception $e) {
if ($transaction !== null) {
} catch (\Exception $e) {
$transaction->rollback();
throw $e;
}
throw $e;
} else {
$result = $this->insertInternal($attributes);
}
return $result;
}
@ -859,21 +859,21 @@ class ActiveRecord extends Model
return false;
}
$db = static::getDb();
$transaction = $this->isTransactional(self::OP_UPDATE) && $db->getTransaction() === null ? $db->beginTransaction() : null;
try {
$result = $this->updateInternal($attributes);
if ($transaction !== null) {
if ($this->isTransactional(self::OP_UPDATE) && $db->getTransaction() === null) {
$transaction = $db->beginTransaction();
try {
$result = $this->updateInternal($attributes);
if ($result === false) {
$transaction->rollback();
} else {
$transaction->commit();
}
}
} catch (\Exception $e) {
if ($transaction !== null) {
} catch (\Exception $e) {
$transaction->rollback();
throw $e;
}
throw $e;
} else {
$result = $this->updateInternal($attributes);
}
return $result;
}
@ -889,6 +889,7 @@ class ActiveRecord extends Model
}
$values = $this->getDirtyAttributes($attributes);
if (empty($values)) {
$this->afterSave(false);
return 0;
}
$condition = $this->getOldPrimaryKey(true);
@ -1010,6 +1011,16 @@ class ActiveRecord extends Model
}
/**
* Sets the value indicating whether the record is new.
* @param boolean $value whether the record is new and should be inserted when calling [[save()]].
* @see getIsNewRecord
*/
public function setIsNewRecord($value)
{
$this->_oldAttributes = $value ? null : $this->_attributes;
}
/**
* Initializes the object.
* This method is called at the end of the constructor.
* The default implementation will trigger an [[EVENT_INIT]] event.
@ -1034,16 +1045,6 @@ class ActiveRecord extends Model
}
/**
* Sets the value indicating whether the record is new.
* @param boolean $value whether the record is new and should be inserted when calling [[save()]].
* @see getIsNewRecord
*/
public function setIsNewRecord($value)
{
$this->_oldAttributes = $value ? null : $this->_attributes;
}
/**
* This method is called at the beginning of inserting or updating a record.
* The default implementation will trigger an [[EVENT_BEFORE_INSERT]] event when `$insert` is true,
* or an [[EVENT_BEFORE_UPDATE]] event if `$insert` is false.

4
framework/yii/db/ActiveRelation.php

@ -279,7 +279,9 @@ class ActiveRelation extends ActiveQuery
// single key
$attribute = reset($this->link);
foreach ($models as $model) {
$values[] = $model[$attribute];
if (($value = $model[$attribute]) !== null) {
$values[] = $value;
}
}
} else {
// composite keys

6
framework/yii/db/Command.php

@ -246,14 +246,14 @@ class Command extends \yii\base\Component
}
/**
* Determines the PDO type for the give PHP data value.
* Determines the PDO type for the given PHP data value.
* @param mixed $data the data whose PDO type is to be determined
* @return integer the PDO type
* @see http://www.php.net/manual/en/pdo.constants.php
*/
private function getPdoType($data)
{
static $typeMap = array(
static $typeMap = array( // php type => PDO type
'boolean' => \PDO::PARAM_BOOL,
'integer' => \PDO::PARAM_INT,
'string' => \PDO::PARAM_STR,
@ -472,7 +472,7 @@ class Command extends \yii\base\Component
* ))->execute();
* ~~~
*
* Not that the values in each row must match the corresponding column names.
* Note that the values in each row must match the corresponding column names.
*
* @param string $table the table that new rows will be inserted into.
* @param array $columns the column names

5
framework/yii/db/Connection.php

@ -201,7 +201,7 @@ class Connection extends Component
public $queryCache = 'cache';
/**
* @var string the charset used for database connection. The property is only used
* for MySQL and PostgreSQL databases. Defaults to null, meaning using default charset
* for MySQL, PostgreSQL and CUBRID databases. Defaults to null, meaning using default charset
* as specified by the database.
*
* Note that if you're using GBK or BIG5 then it's highly recommended to
@ -244,6 +244,7 @@ class Connection extends Component
'oci' => 'yii\db\oci\Schema', // Oracle driver
'mssql' => 'yii\db\mssql\Schema', // older MSSQL driver on MS Windows hosts
'dblib' => 'yii\db\mssql\Schema', // dblib drivers on GNU/Linux (and maybe other OSes) hosts
'cubrid' => 'yii\db\cubrid\Schema', // CUBRID
);
/**
* @var Transaction the currently active transaction
@ -361,7 +362,7 @@ class Connection extends Component
if ($this->emulatePrepare !== null && constant('PDO::ATTR_EMULATE_PREPARES')) {
$this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, $this->emulatePrepare);
}
if ($this->charset !== null && in_array($this->getDriverName(), array('pgsql', 'mysql', 'mysqli'))) {
if ($this->charset !== null && in_array($this->getDriverName(), array('pgsql', 'mysql', 'mysqli', 'cubrid'))) {
$this->pdo->exec('SET NAMES ' . $this->pdo->quote($this->charset));
}
$this->trigger(self::EVENT_AFTER_OPEN);

2
framework/yii/db/DataReader.php

@ -40,7 +40,7 @@ use yii\base\InvalidCallException;
* for more details about possible fetch mode.
*
* @property integer $columnCount The number of columns in the result set. This property is read-only.
* @property mixed $fetchMode Fetch mode. This property is write-only.
* @property integer $fetchMode Fetch mode. This property is write-only.
* @property boolean $isClosed Whether the reader is closed or not. This property is read-only.
* @property integer $rowCount Number of rows contained in the result. This property is read-only.
*

17
framework/yii/db/QueryBuilder.php

@ -134,7 +134,7 @@ class QueryBuilder extends \yii\base\Object
* ))->execute();
* ~~~
*
* Not that the values in each row must match the corresponding column names.
* Note that the values in each row must match the corresponding column names.
*
* @param string $table the table that new rows will be inserted into.
* @param array $columns the column names
@ -491,6 +491,7 @@ class QueryBuilder extends \yii\base\Object
* physical types):
*
* - `pk`: an auto-incremental primary key type, will be converted into "int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY"
* - `bigpk`: an auto-incremental primary key type, will be converted into "bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY"
* - `string`: string type, will be converted into "varchar(255)"
* - `text`: a long string type, will be converted into "text"
* - `smallint`: a small integer type, will be converted into "smallint(6)"
@ -584,7 +585,7 @@ class QueryBuilder extends \yii\base\Object
foreach ($tables as $i => $table) {
if (strpos($table, '(') === false) {
if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/i', $table, $matches)) { // with alias
if (preg_match('/^(.*?)(?i:\s+as|)\s+([^ ]+)$/', $table, $matches)) { // with alias
$tables[$i] = $this->db->quoteTableName($matches[1]) . ' ' . $this->db->quoteTableName($matches[2]);
} else {
$tables[$i] = $this->db->quoteTableName($table);
@ -618,7 +619,7 @@ class QueryBuilder extends \yii\base\Object
// 0:join type, 1:table name, 2:on-condition
$table = $join[1];
if (strpos($table, '(') === false) {
if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/', $table, $matches)) { // with alias
if (preg_match('/^(.*?)(?i:\s+as|)\s+([^ ]+)$/', $table, $matches)) { // with alias
$table = $this->db->quoteTableName($matches[1]) . ' ' . $this->db->quoteTableName($matches[2]);
} else {
$table = $this->db->quoteTableName($table);
@ -912,11 +913,6 @@ class QueryBuilder extends \yii\base\Object
protected function buildCompositeInCondition($operator, $columns, $values, &$params)
{
foreach ($columns as $i => $column) {
if (strpos($column, '(') === false) {
$columns[$i] = $this->db->quoteColumnName($column);
}
}
$vss = array();
foreach ($values as $value) {
$vs = array();
@ -931,6 +927,11 @@ class QueryBuilder extends \yii\base\Object
}
$vss[] = '(' . implode(', ', $vs) . ')';
}
foreach ($columns as $i => $column) {
if (strpos($column, '(') === false) {
$columns[$i] = $this->db->quoteColumnName($column);
}
}
return '(' . implode(', ', $columns) . ") $operator (" . implode(', ', $vss) . ')';
}

3
framework/yii/db/Schema.php

@ -35,6 +35,7 @@ abstract class Schema extends Object
* The followings are the supported abstract column data types.
*/
const TYPE_PK = 'pk';
const TYPE_BIGPK = 'bigpk';
const TYPE_STRING = 'string';
const TYPE_TEXT = 'text';
const TYPE_SMALLINT = 'smallint';
@ -216,7 +217,7 @@ abstract class Schema extends Object
* This method should be overridden by child classes in order to support this feature
* because the default implementation simply throws an exception.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
* @return array all table names in the database. The names have NO the schema name prefix.
* @return array all table names in the database. The names have NO schema name prefix.
* @throws NotSupportedException if this method is called
*/
protected function findTableNames($schema = '')

6
framework/yii/db/TableSchema.php

@ -21,12 +21,6 @@ use yii\base\InvalidParamException;
class TableSchema extends Object
{
/**
* @var string name of the catalog (database) that this table belongs to.
* Defaults to null, meaning no catalog (or the current database).
* This property is only meaningful for MSSQL.
*/
public $catalogName;
/**
* @var string name of the schema that this table belongs to.
*/
public $schemaName;

117
framework/yii/db/cubrid/QueryBuilder.php

@ -0,0 +1,117 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\cubrid;
use yii\base\InvalidParamException;
/**
* QueryBuilder is the query builder for CUBRID databases (version 9.1.x and higher).
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class QueryBuilder extends \yii\db\QueryBuilder
{
/**
* @var array mapping from abstract column types (keys) to physical column types (values).
*/
public $typeMap = array(
Schema::TYPE_PK => 'int NOT NULL AUTO_INCREMENT PRIMARY KEY',
Schema::TYPE_BIGPK => 'bigint NOT NULL AUTO_INCREMENT PRIMARY KEY',
Schema::TYPE_STRING => 'varchar(255)',
Schema::TYPE_TEXT => 'varchar',
Schema::TYPE_SMALLINT => 'smallint',
Schema::TYPE_INTEGER => 'int',
Schema::TYPE_BIGINT => 'bigint',
Schema::TYPE_FLOAT => 'float(7)',
Schema::TYPE_DECIMAL => 'decimal(10,0)',
Schema::TYPE_DATETIME => 'datetime',
Schema::TYPE_TIMESTAMP => 'timestamp',
Schema::TYPE_TIME => 'time',
Schema::TYPE_DATE => 'date',
Schema::TYPE_BINARY => 'blob',
Schema::TYPE_BOOLEAN => 'smallint',
Schema::TYPE_MONEY => 'decimal(19,4)',
);
/**
* Creates a SQL statement for resetting the sequence value of a table's primary key.
* The sequence will be reset such that the primary key of the next new row inserted
* will have the specified value or 1.
* @param string $tableName the name of the table whose primary key sequence will be reset
* @param mixed $value the value for the primary key of the next new row inserted. If this is not set,
* the next new row's primary key will have a value 1.
* @return string the SQL statement for resetting sequence
* @throws InvalidParamException if the table does not exist or there is no sequence associated with the table.
*/
public function resetSequence($tableName, $value = null)
{
$table = $this->db->getTableSchema($tableName);
if ($table !== null && $table->sequenceName !== null) {
$tableName = $this->db->quoteTableName($tableName);
if ($value === null) {
$key = reset($table->primaryKey);
$value = (int)$this->db->createCommand("SELECT MAX(`$key`) FROM " . $this->db->schema->quoteTableName($tableName))->queryScalar() + 1;
} else {
$value = (int)$value;
}
return "ALTER TABLE " . $this->db->schema->quoteTableName($tableName) . " AUTO_INCREMENT=$value;";
} elseif ($table === null) {
throw new InvalidParamException("Table not found: $tableName");
} else {
throw new InvalidParamException("There is not sequence associated with table '$tableName'.");
}
}
/**
* Generates a batch INSERT SQL statement.
* For example,
*
* ~~~
* $connection->createCommand()->batchInsert('tbl_user', array('name', 'age'), array(
* array('Tom', 30),
* array('Jane', 20),
* array('Linda', 25),
* ))->execute();
* ~~~
*
* Note that the values in each row must match the corresponding column names.
*
* @param string $table the table that new rows will be inserted into.
* @param array $columns the column names
* @param array $rows the rows to be batch inserted into the table
* @return string the batch INSERT SQL statement
*/
public function batchInsert($table, $columns, $rows)
{
if (($tableSchema = $this->db->getTableSchema($table)) !== null) {
$columnSchemas = $tableSchema->columns;
} else {
$columnSchemas = array();
}
foreach ($columns as $i => $name) {
$columns[$i] = $this->db->quoteColumnName($name);
}
$values = array();
foreach ($rows as $row) {
$vs = array();
foreach ($row as $i => $value) {
if (!is_array($value) && isset($columnSchemas[$columns[$i]])) {
$value = $columnSchemas[$columns[$i]]->typecast($value);
}
$vs[] = is_string($value) ? $this->db->quoteValue($value) : $value;
}
$values[] = '(' . implode(', ', $vs) . ')';
}
return 'INSERT INTO ' . $this->db->quoteTableName($table)
. ' (' . implode(', ', $columns) . ') VALUES ' . implode(', ', $values);
}
}

240
framework/yii/db/cubrid/Schema.php

@ -0,0 +1,240 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\cubrid;
use yii\db\Expression;
use yii\db\TableSchema;
use yii\db\ColumnSchema;
/**
* Schema is the class for retrieving metadata from a CUBRID database (version 9.1.x and higher).
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class Schema extends \yii\db\Schema
{
/**
* @var array mapping from physical column types (keys) to abstract column types (values)
* Please refer to [CUBRID manual](http://www.cubrid.org/manual/91/en/sql/datatype.html) for
* details on data types.
*/
public $typeMap = array(
// Numeric data types
'short' => self::TYPE_SMALLINT,
'smallint' => self::TYPE_SMALLINT,
'int' => self::TYPE_INTEGER,
'integer' => self::TYPE_INTEGER,
'bigint' => self::TYPE_BIGINT,
'numeric' => self::TYPE_DECIMAL,
'decimal' => self::TYPE_DECIMAL,
'float' => self::TYPE_FLOAT,
'real' => self::TYPE_FLOAT,
'double' => self::TYPE_FLOAT,
'double precision' => self::TYPE_FLOAT,
'monetary' => self::TYPE_MONEY,
// Date/Time data types
'date' => self::TYPE_DATE,
'time' => self::TYPE_TIME,
'timestamp' => self::TYPE_TIMESTAMP,
'datetime' => self::TYPE_DATETIME,
// String data types
'char' => self::TYPE_STRING,
'varchar' => self::TYPE_STRING,
'char varying' => self::TYPE_STRING,
'nchar' => self::TYPE_STRING,
'nchar varying' => self::TYPE_STRING,
'string' => self::TYPE_STRING,
// BLOB/CLOB data types
'blob' => self::TYPE_BINARY,
'clob' => self::TYPE_BINARY,
// Bit string data types
'bit' => self::TYPE_STRING,
'bit varying' => self::TYPE_STRING,
// Collection data types (considered strings for now)
'set' => self::TYPE_STRING,
'multiset' => self::TYPE_STRING,
'list' => self::TYPE_STRING,
'sequence' => self::TYPE_STRING,
'enum' => self::TYPE_STRING,
);
/**
* Quotes a table name for use in a query.
* A simple table name has no schema prefix.
* @param string $name table name
* @return string the properly quoted table name
*/
public function quoteSimpleTableName($name)
{
return strpos($name, '"') !== false ? $name : '"' . $name . '"';
}
/**
* Quotes a column name for use in a query.
* A simple column name has no prefix.
* @param string $name column name
* @return string the properly quoted column name
*/
public function quoteSimpleColumnName($name)
{
return strpos($name, '"') !== false || $name === '*' ? $name : '"' . $name . '"';
}
/**
* Quotes a string value for use in a query.
* Note that if the parameter is not a string, it will be returned without change.
* @param string $str string to be quoted
* @return string the properly quoted string
* @see http://www.php.net/manual/en/function.PDO-quote.php
*/
public function quoteValue($str)
{
if (!is_string($str)) {
return $str;
}
$this->db->open();
// workaround for broken PDO::quote() implementation in CUBRID 9.1.0 http://jira.cubrid.org/browse/APIS-658
if (version_compare($this->db->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION), '9.1.0', '<=')) {
return "'" . addcslashes(str_replace("'", "''", $str), "\000\n\r\\\032") . "'";
} else {
return $this->db->pdo->quote($str);
}
}
/**
* Creates a query builder for the CUBRID database.
* @return QueryBuilder query builder instance
*/
public function createQueryBuilder()
{
return new QueryBuilder($this->db);
}
/**
* Loads the metadata for the specified table.
* @param string $name table name
* @return TableSchema driver dependent table metadata. Null if the table does not exist.
*/
protected function loadTableSchema($name)
{
$this->db->open();
$tableInfo = $this->db->pdo->cubrid_schema(\PDO::CUBRID_SCH_TABLE, $name);
if (isset($tableInfo[0]['NAME'])) {
$table = new TableSchema();
$table->name = $tableInfo[0]['NAME'];
$sql = 'SHOW FULL COLUMNS FROM ' . $this->quoteSimpleTableName($table->name);
$columns = $this->db->createCommand($sql)->queryAll();
foreach ($columns as $info) {
$column = $this->loadColumnSchema($info);
$table->columns[$column->name] = $column;
if ($column->isPrimaryKey) {
$table->primaryKey[] = $column->name;
if ($column->autoIncrement) {
$table->sequenceName = '';
}
}
}
$foreignKeys = $this->db->pdo->cubrid_schema(\PDO::CUBRID_SCH_IMPORTED_KEYS, $table->name);
foreach($foreignKeys as $key) {
if (isset($table->foreignKeys[$key['FK_NAME']])) {
$table->foreignKeys[$key['FK_NAME']][$key['FKCOLUMN_NAME']] = $key['PKCOLUMN_NAME'];
} else {
$table->foreignKeys[$key['FK_NAME']] = array(
$key['PKTABLE_NAME'],
$key['FKCOLUMN_NAME'] => $key['PKCOLUMN_NAME']
);
}
}
$table->foreignKeys = array_values($table->foreignKeys);
return $table;
} else {
return null;
}
}
/**
* Loads the column information into a [[ColumnSchema]] object.
* @param array $info column information
* @return ColumnSchema the column schema object
*/
protected function loadColumnSchema($info)
{
$column = new ColumnSchema();
$column->name = $info['Field'];
$column->allowNull = $info['Null'] === 'YES';
$column->isPrimaryKey = strpos($info['Key'], 'PRI') !== false;
$column->autoIncrement = stripos($info['Extra'], 'auto_increment') !== false;
$column->dbType = strtolower($info['Type']);
$column->unsigned = strpos($column->dbType, 'unsigned') !== false;
$column->type = self::TYPE_STRING;
if (preg_match('/^([\w ]+)(?:\(([^\)]+)\))?/', $column->dbType, $matches)) {
$type = $matches[1];
if (isset($this->typeMap[$type])) {
$column->type = $this->typeMap[$type];
}
if (!empty($matches[2])) {
if ($type === 'enum') {
$values = explode(',', $matches[2]);
foreach ($values as $i => $value) {
$values[$i] = trim($value, "'");
}
$column->enumValues = $values;
} else {
$values = explode(',', $matches[2]);
$column->size = $column->precision = (int)$values[0];
if (isset($values[1])) {
$column->scale = (int)$values[1];
}
}
}
}
$column->phpType = $this->getColumnPhpType($column);
if ($column->type === 'timestamp' && $info['Default'] === 'CURRENT_TIMESTAMP' ||
$column->type === 'datetime' && $info['Default'] === 'SYS_DATETIME' ||
$column->type === 'date' && $info['Default'] === 'SYS_DATE' ||
$column->type === 'time' && $info['Default'] === 'SYS_TIME'
) {
$column->defaultValue = new Expression($info['Default']);
} else {
$column->defaultValue = $column->typecast($info['Default']);
}
return $column;
}
/**
* Returns all table names in the database.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
* @return array all table names in the database. The names have NO schema name prefix.
*/
protected function findTableNames($schema = '')
{
$this->db->open();
$tables = $this->db->pdo->cubrid_schema(\PDO::CUBRID_SCH_TABLE);
$tableNames = array();
foreach($tables as $table) {
// do not list system tables
if ($table['TYPE'] != 0) {
$tableNames[] = $table['NAME'];
}
}
return $tableNames;
}
}

1
framework/yii/db/mssql/QueryBuilder.php

@ -22,6 +22,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
*/
public $typeMap = array(
Schema::TYPE_PK => 'int IDENTITY PRIMARY KEY',
Schema::TYPE_BIGPK => 'bigint IDENTITY PRIMARY KEY',
Schema::TYPE_STRING => 'varchar(255)',
Schema::TYPE_TEXT => 'text',
Schema::TYPE_SMALLINT => 'smallint(6)',

5
framework/yii/db/mssql/Schema.php

@ -7,7 +7,6 @@
namespace yii\db\mssql;
use yii\db\TableSchema;
use yii\db\ColumnSchema;
/**
@ -332,10 +331,8 @@ SQL;
/**
* Returns all table names in the database.
* This method should be overridden by child classes in order to support this feature
* because the default implementation simply throws an exception.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
* @return array all table names in the database. The names have NO the schema name prefix.
* @return array all table names in the database. The names have NO schema name prefix.
*/
protected function findTableNames($schema = '')
{

23
framework/yii/db/mssql/TableSchema.php

@ -0,0 +1,23 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2011 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\db\mssql;
/**
* TableSchema represents the metadata of a database table.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class TableSchema extends \yii\db\TableSchema
{
/**
* @var string name of the catalog (database) that this table belongs to.
* Defaults to null, meaning no catalog (or the current database).
*/
public $catalogName;
}

3
framework/yii/db/mysql/QueryBuilder.php

@ -23,6 +23,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
*/
public $typeMap = array(
Schema::TYPE_PK => 'int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY',
Schema::TYPE_BIGPK => 'bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY',
Schema::TYPE_STRING => 'varchar(255)',
Schema::TYPE_TEXT => 'text',
Schema::TYPE_SMALLINT => 'smallint(6)',
@ -152,7 +153,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
* ))->execute();
* ~~~
*
* Not that the values in each row must match the corresponding column names.
* Note that the values in each row must match the corresponding column names.
*
* @param string $table the table that new rows will be inserted into.
* @param array $columns the column names

4
framework/yii/db/mysql/Schema.php

@ -236,10 +236,8 @@ class Schema extends \yii\db\Schema
/**
* Returns all table names in the database.
* This method should be overridden by child classes in order to support this feature
* because the default implementation simply throws an exception.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
* @return array all table names in the database. The names have NO the schema name prefix.
* @return array all table names in the database. The names have NO schema name prefix.
*/
protected function findTableNames($schema = '')
{

3
framework/yii/db/pgsql/QueryBuilder.php

@ -21,7 +21,8 @@ class QueryBuilder extends \yii\db\QueryBuilder
* @var array mapping from abstract column types (keys) to physical column types (values).
*/
public $typeMap = array(
Schema::TYPE_PK => 'serial not null primary key',
Schema::TYPE_PK => 'serial NOT NULL PRIMARY KEY',
Schema::TYPE_BIGPK => 'bigserial NOT NULL PRIMARY KEY',
Schema::TYPE_STRING => 'varchar(255)',
Schema::TYPE_TEXT => 'text',
Schema::TYPE_SMALLINT => 'smallint',

33
framework/yii/db/pgsql/Schema.php

@ -129,6 +129,35 @@ class Schema extends \yii\db\Schema
}
/**
* Returns all table names in the database.
* @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
* If not empty, the returned table names will be prefixed with the schema name.
* @return array all table names in the database.
*/
protected function findTableNames($schema = '')
{
if ($schema === '') {
$schema = $this->defaultSchema;
}
$sql = <<<EOD
SELECT table_name, table_schema FROM information_schema.tables
WHERE table_schema=:schema AND table_type='BASE TABLE'
EOD;
$command = $this->db->createCommand($sql);
$command->bindParam(':schema', $schema);
$rows = $command->queryAll();
$names = array();
foreach ($rows as $row) {
if ($schema === $this->defaultSchema) {
$names[] = $row['table_name'];
} else {
$names[] = $row['table_schema'] . '.' . $row['table_name'];
}
}
return $names;
}
/**
* Collects the foreign key column details for the given table.
* @param TableSchema $table the table metadata
*/
@ -171,7 +200,7 @@ SQL;
}
$citem = array($foreignTable);
foreach ($columns as $idx => $column) {
$citem[] = array($fcolumns[$idx] => $column);
$citem[$fcolumns[$idx]] = $column;
}
$table->foreignKeys[] = $citem;
}
@ -226,7 +255,7 @@ SELECT
information_schema._pg_char_max_length(information_schema._pg_truetypid(a, t), information_schema._pg_truetypmod(a, t))
AS numeric
) AS size,
a.attnum = any (ct.conkey) as is_pkey
a.attnum = any (ct.conkey) as is_pkey
FROM
pg_class c
LEFT JOIN pg_attribute a ON a.attrelid = c.oid

1
framework/yii/db/sqlite/QueryBuilder.php

@ -24,6 +24,7 @@ class QueryBuilder extends \yii\db\QueryBuilder
*/
public $typeMap = array(
Schema::TYPE_PK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL',
Schema::TYPE_BIGPK => 'integer PRIMARY KEY AUTOINCREMENT NOT NULL',
Schema::TYPE_STRING => 'varchar(255)',
Schema::TYPE_TEXT => 'text',
Schema::TYPE_SMALLINT => 'smallint',

8
framework/yii/db/sqlite/Schema.php

@ -126,7 +126,13 @@ class Schema extends \yii\db\Schema
$sql = "PRAGMA foreign_key_list(" . $this->quoteSimpleTableName($table->name) . ')';
$keys = $this->db->createCommand($sql)->queryAll();
foreach ($keys as $key) {
$table->foreignKeys[] = array($key['table'], $key['from'] => $key['to']);
$id = (int)$key['id'];
if (!isset($table->foreignKeys[$id])) {
$table->foreignKeys[$id] = array($key['table'], $key['from'] => $key['to']);
} else {
// composite FK
$table->foreignKeys[$id][$key['from']] = $key['to'];
}
}
}

1
framework/yii/debug/assets/main.css

@ -131,6 +131,7 @@ ul.trace {
margin: 2px 0 0 0;
padding: 0;
list-style: none;
white-space: normal;
}
.callout-danger {

22
framework/yii/gii/Generator.php

@ -325,6 +325,28 @@ abstract class Generator extends Model
}
/**
* An inline validator that checks if the attribute value refers to a valid namespaced class name.
* The validator will check if the directory containing the new class file exist or not.
* @param string $attribute the attribute being validated
* @param array $params the validation options
*/
public function validateNewClass($attribute, $params)
{
$class = ltrim($this->$attribute, '\\');
if (($pos = strrpos($class, '\\')) === false) {
$this->addError($attribute, "The class name must contain fully qualified namespace name.");
} else {
$ns = substr($class, 0, $pos);
$path = Yii::getAlias('@' . str_replace('\\', '/', $ns), false);
if ($path === false) {
$this->addError($attribute, "The class namespace is invalid: $ns");
} elseif (!is_dir($path)) {
$this->addError($attribute, "Please make sure the directory containing this class exists: $path");
}
}
}
/**
* @param string $value the attribute to be validated
* @return boolean whether the value is a reserved PHP keyword.
*/

33
framework/yii/gii/controllers/DefaultController.php

@ -7,6 +7,7 @@
namespace yii\gii\controllers;
use Yii;
use yii\web\Controller;
use yii\web\HttpException;
@ -86,6 +87,26 @@ class DefaultController extends Controller
throw new HttpException(404, "Code file not found: $file");
}
/**
* Runs an action defined in the generator.
* Given an action named "xyz", the method "actionXyz()" in the generator will be called.
* If the method does not exist, a 400 HTTP exception will be thrown.
* @param string $id the ID of the generator
* @param string $name the action name
* @return mixed the result of the action.
* @throws HttpException if the action method does not exist.
*/
public function actionAction($id, $name)
{
$generator = $this->loadGenerator($id);
$method = 'action' . $name;
if (method_exists($generator, $method)) {
return $generator->$method();
} else {
throw new HttpException(400, "Unknown generator action: $name");
}
}
public function createUrl($route, $params = array())
{
if (!isset($params['id']) && $this->generator !== null) {
@ -99,6 +120,18 @@ class DefaultController extends Controller
return parent::createUrl($route, $params);
}
public function createActionUrl($name, $params = array())
{
foreach ($this->module->generators as $id => $generator) {
if ($generator === $this->generator) {
$params['id'] = $id;
break;
}
}
$params['name'] = $name;
return parent::createUrl('action', $params);
}
/**
* Loads the generator with the specified ID.
* @param string $id the ID of the generator to be loaded.

2
framework/yii/gii/generators/controller/Generator.php

@ -78,7 +78,7 @@ class Generator extends \yii\gii\Generator
'baseClass' => 'Base Class',
'controller' => 'Controller ID',
'actions' => 'Action IDs',
'ns' => 'Namespace',
'ns' => 'Controller Namespace',
);
}

325
framework/yii/gii/generators/crud/Generator.php

@ -7,8 +7,12 @@
namespace yii\gii\generators\crud;
use Yii;
use yii\base\Model;
use yii\db\ActiveRecord;
use yii\db\Schema;
use yii\gii\CodeFile;
use yii\helpers\Inflector;
use yii\web\Controller;
/**
@ -19,8 +23,11 @@ use yii\web\Controller;
class Generator extends \yii\gii\Generator
{
public $modelClass;
public $controllerID;
public $moduleID;
public $controllerClass;
public $baseControllerClass = 'yii\web\Controller';
public $indexWidgetType = 'grid';
public $searchModelClass;
public function getName()
{
@ -36,13 +43,16 @@ class Generator extends \yii\gii\Generator
public function rules()
{
return array_merge(parent::rules(), array(
array('modelClass, controllerID, baseControllerClass', 'filter', 'filter' => 'trim'),
array('modelClass, controllerID, baseControllerClass', 'required'),
array('modelClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
array('moduleID, controllerClass, modelClass, searchModelClass, baseControllerClass', 'filter', 'filter' => 'trim'),
array('modelClass, searchModelClass, controllerClass, baseControllerClass, indexWidgetType', 'required'),
array('modelClass, controllerClass, baseControllerClass, searchModelClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
array('modelClass', 'validateClass', 'params' => array('extends' => ActiveRecord::className())),
array('controllerID', 'match', 'pattern' => '/^[a-z\\-\\/]*$/', 'message' => 'Only a-z, dashes (-) and slashes (/) are allowed.'),
array('baseControllerClass', 'match', 'pattern' => '/^[\w\\\\]*$/', 'message' => 'Only word characters and backslashes are allowed.'),
array('baseControllerClass', 'validateClass', 'params' => array('extends' => Controller::className())),
array('controllerClass', 'match', 'pattern' => '/Controller$/', 'message' => 'Controller class name must be suffixed with "Controller".'),
array('controllerClass, searchModelClass', 'validateNewClass'),
array('indexWidgetType', 'in', 'range' => array('grid', 'list')),
array('modelClass', 'validateModelClass'),
array('moduleID', 'validateModuleID'),
));
}
@ -50,8 +60,11 @@ class Generator extends \yii\gii\Generator
{
return array_merge(parent::attributeLabels(), array(
'modelClass' => 'Model Class',
'controllerID' => 'Controller ID',
'moduleID' => 'Module ID',
'controllerClass' => 'Controller Class',
'baseControllerClass' => 'Base Controller Class',
'indexWidgetType' => 'Widget Used in Index Page',
'searchModelClass' => 'Search Model Class',
));
}
@ -63,15 +76,16 @@ class Generator extends \yii\gii\Generator
return array(
'modelClass' => 'This is the ActiveRecord class associated with the table that CRUD will be built upon.
You should provide a fully qualified class name, e.g., <code>app\models\Post</code>.',
'controllerID' => 'CRUD controllers are often named after the model class name that they are dealing with.
Controller ID should be in lower case and may contain module ID(s) separated by slashes. For example:
<ul>
<li><code>order</code> generates <code>OrderController.php</code></li>
<li><code>order-item</code> generates <code>OrderItemController.php</code></li>
<li><code>admin/user</code> generates <code>UserController.php</code> within the <code>admin</code> module.</li>
</ul>',
'controllerClass' => 'This is the name of the controller class to be generated. You should
provide a fully qualified namespaced class, .e.g, <code>app\controllers\PostController</code>.',
'baseControllerClass' => 'This is the class that the new CRUD controller class will extend from.
You should provide a fully qualified class name, e.g., <code>yii\web\Controller</code>.',
'moduleID' => 'This is the ID of the module that the generated controller will belong to.
If not set, it means the controller will belong to the application.',
'indexWidgetType' => 'This is the widget type to be used in the index page to display list of the models.
You may choose either <code>GridView</code> or <code>ListView</code>',
'searchModelClass' => 'This is the class representing the data being collecting in the search form.
A fully qualified namespaced class name is required, e.g., <code>app\models\search\PostSearch</code>.',
);
}
@ -87,7 +101,27 @@ class Generator extends \yii\gii\Generator
*/
public function stickyAttributes()
{
return array('baseControllerClass');
return array('baseControllerClass', 'moduleID', 'indexWidgetType');
}
public function validateModelClass()
{
/** @var ActiveRecord $class */
$class = $this->modelClass;
$pk = $class::primaryKey();
if (empty($pk)) {
$this->addError('modelClass', "The table associated with $class must have primary key(s).");
}
}
public function validateModuleID()
{
if (!empty($this->moduleID)) {
$module = Yii::$app->getModule($this->moduleID);
if ($module === null) {
$this->addError('moduleID', "Module '{$this->moduleID}' does not exist.");
}
}
}
/**
@ -95,22 +129,261 @@ class Generator extends \yii\gii\Generator
*/
public function generate()
{
$files = array();
$files[] = new CodeFile(
$this->controllerFile,
$this->render('controller.php')
$controllerFile = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->controllerClass, '\\')) . '.php');
$searchModel = Yii::getAlias('@' . str_replace('\\', '/', ltrim($this->searchModelClass, '\\') . '.php'));
$files = array(
new CodeFile($controllerFile, $this->render('controller.php')),
new CodeFile($searchModel, $this->render('search.php')),
);
$files = scandir($this->getTemplatePath());
foreach ($files as $file) {
if (is_file($templatePath . '/' . $file) && CFileHelper::getExtension($file) === 'php' && $file !== 'controller.php') {
$files[] = new CodeFile(
$this->viewPath . DIRECTORY_SEPARATOR . $file,
$this->render($templatePath . '/' . $file)
);
$viewPath = $this->getViewPath();
$templatePath = $this->getTemplatePath() . '/views';
foreach (scandir($templatePath) as $file) {
if (is_file($templatePath . '/' . $file) && pathinfo($file, PATHINFO_EXTENSION) === 'php') {
$files[] = new CodeFile("$viewPath/$file", $this->render("views/$file"));
}
}
return $files;
}
/**
* @return string the controller ID (without the module ID prefix)
*/
public function getControllerID()
{
$pos = strrpos($this->controllerClass, '\\');
$class = substr(substr($this->controllerClass, $pos + 1), 0, -10);
return Inflector::camel2id($class);
}
/**
* @return string the action view file path
*/
public function getViewPath()
{
$module = empty($this->moduleID) ? Yii::$app : Yii::$app->getModule($this->moduleID);
return $module->getViewPath() . '/' . $this->getControllerID() ;
}
public function getNameAttribute()
{
/** @var \yii\db\ActiveRecord $class */
$class = $this->modelClass;
foreach ($class::getTableSchema()->columnNames as $name) {
if (!strcasecmp($name, 'name') || !strcasecmp($name, 'title')) {
return $name;
}
}
$pk = $class::primaryKey();
return $pk[0];
}
/**
* @param string $attribute
* @return string
*/
public function generateActiveField($attribute)
{
$tableSchema = $this->getTableSchema();
if (!isset($tableSchema->columns[$attribute])) {
return "\$form->field(\$model, '$attribute');";
}
$column = $tableSchema->columns[$attribute];
if ($column->phpType === 'boolean') {
return "\$form->field(\$model, '$attribute')->checkbox();";
} elseif ($column->type === 'text') {
return "\$form->field(\$model, '$attribute')->textarea(array('rows' => 6));";
} else {
if (preg_match('/^(password|pass|passwd|passcode)$/i', $column->name)) {
$input = 'passwordInput';
} else {
$input = 'textInput';
}
if ($column->phpType !== 'string' || $column->size === null) {
return "\$form->field(\$model, '$attribute')->$input();";
} else {
return "\$form->field(\$model, '$attribute')->$input(array('maxlength' => $column->size));";
}
}
}
/**
* @param string $attribute
* @return string
*/
public function generateActiveSearchField($attribute)
{
$tableSchema = $this->getTableSchema();
$column = $tableSchema->columns[$attribute];
if ($column->phpType === 'boolean') {
return "\$form->field(\$model, '$attribute')->checkbox();";
} else {
return "\$form->field(\$model, '$attribute');";
}
}
/**
* @param \yii\db\ColumnSchema $column
* @return string
*/
public function generateColumnFormat($column)
{
if ($column->phpType === 'boolean') {
return 'boolean';
} elseif ($column->type === 'text') {
return 'ntext';
} elseif (stripos($column->name, 'time') !== false && $column->phpType === 'integer') {
return 'datetime';
} elseif (stripos($column->name, 'email') !== false) {
return 'email';
} elseif (stripos($column->name, 'url') !== false) {
return 'url';
} else {
return 'text';
}
}
/**
* Generates validation rules for the search model.
* @return array the generated validation rules
*/
public function generateSearchRules()
{
$table = $this->getTableSchema();
$types = array();
foreach ($table->columns as $column) {
switch ($column->type) {
case Schema::TYPE_SMALLINT:
case Schema::TYPE_INTEGER:
case Schema::TYPE_BIGINT:
$types['integer'][] = $column->name;
break;
case Schema::TYPE_BOOLEAN:
$types['boolean'][] = $column->name;
break;
case Schema::TYPE_FLOAT:
case Schema::TYPE_DECIMAL:
case Schema::TYPE_MONEY:
$types['number'][] = $column->name;
break;
case Schema::TYPE_DATE:
case Schema::TYPE_TIME:
case Schema::TYPE_DATETIME:
case Schema::TYPE_TIMESTAMP:
default:
$types['safe'][] = $column->name;
break;
}
}
$rules = array();
foreach ($types as $type => $columns) {
$rules[] = "array('" . implode(', ', $columns) . "', '$type')";
}
return $rules;
}
public function getSearchAttributes()
{
return $this->getTableSchema()->getColumnNames();
}
/**
* Generates the attribute labels for the search model.
* @return array the generated attribute labels (name => label)
*/
public function generateSearchLabels()
{
$table = $this->getTableSchema();
$labels = array();
foreach ($table->columns as $column) {
if (!strcasecmp($column->name, 'id')) {
$labels[$column->name] = 'ID';
} else {
$label = Inflector::camel2words($column->name);
if (strcasecmp(substr($label, -3), ' id') === 0) {
$label = substr($label, 0, -3) . ' ID';
}
$labels[$column->name] = $label;
}
}
return $labels;
}
public function generateSearchConditions()
{
$table = $this->getTableSchema();
$conditions = array();
foreach ($table->columns as $column) {
switch ($column->type) {
case Schema::TYPE_SMALLINT:
case Schema::TYPE_INTEGER:
case Schema::TYPE_BIGINT:
case Schema::TYPE_BOOLEAN:
case Schema::TYPE_FLOAT:
case Schema::TYPE_DECIMAL:
case Schema::TYPE_MONEY:
case Schema::TYPE_DATE:
case Schema::TYPE_TIME:
case Schema::TYPE_DATETIME:
case Schema::TYPE_TIMESTAMP:
$conditions[] = "\$this->addCondition(\$query, '{$column->name}');";
break;
default:
$conditions[] = "\$this->addCondition(\$query, '{$column->name}', true);";
break;
}
}
return $conditions;
}
public function generateUrlParams()
{
$pks = $this->getTableSchema()->primaryKey;
if (count($pks) === 1) {
return "'id' => \$model->{$pks[0]}";
} else {
$params = array();
foreach ($pks as $pk) {
$params[] = "'$pk' => \$model->$pk";
}
return implode(', ', $params);
}
}
public function generateActionParams()
{
$pks = $this->getTableSchema()->primaryKey;
if (count($pks) === 1) {
return '$id';
} else {
return '$' . implode(', $', $pks);
}
}
public function generateActionParamComments()
{
$table = $this->getTableSchema();
$pks = $table->primaryKey;
if (count($pks) === 1) {
return array('@param ' . $table->columns[$pks[0]]->phpType . ' $id');
} else {
$params = array();
foreach ($pks as $pk) {
$params[] = '@param ' . $table->columns[$pk]->phpType . ' $' . $pk;
}
return $params;
}
}
public function getTableSchema()
{
/** @var ActiveRecord $class */
$class = $this->modelClass;
return $class::getTableSchema();
}
}

8
framework/yii/gii/generators/crud/form.php

@ -6,5 +6,11 @@
*/
echo $form->field($generator, 'modelClass');
echo $form->field($generator, 'controllerID');
echo $form->field($generator, 'searchModelClass');
echo $form->field($generator, 'controllerClass');
echo $form->field($generator, 'baseControllerClass');
echo $form->field($generator, 'moduleID');
echo $form->field($generator, 'indexWidgetType')->dropDownList(array(
'grid' => 'GridView',
'list' => 'ListView',
));

154
framework/yii/gii/generators/crud/templates/controller.php

@ -1,8 +1,152 @@
<?php
use yii\helpers\StringHelper;
/**
* Created by JetBrains PhpStorm.
* User: qiang
* Date: 8/26/13
* Time: 5:22 PM
* To change this template use File | Settings | File Templates.
* This is the template for generating a CRUD controller class file.
*
* @var yii\base\View $this
* @var yii\gii\generators\crud\Generator $generator
*/
$controllerClass = StringHelper::basename($generator->controllerClass);
$modelClass = StringHelper::basename($generator->modelClass);
$searchModelClass = StringHelper::basename($generator->searchModelClass);
$pks = $generator->getTableSchema()->primaryKey;
$urlParams = $generator->generateUrlParams();
$actionParams = $generator->generateActionParams();
$actionParamComments = $generator->generateActionParamComments();
echo "<?php\n";
?>
namespace <?php echo StringHelper::dirname(ltrim($generator->controllerClass, '\\')); ?>;
use <?php echo ltrim($generator->modelClass, '\\'); ?>;
use <?php echo ltrim($generator->searchModelClass, '\\'); ?>;
use yii\data\ActiveDataProvider;
use <?php echo ltrim($generator->baseControllerClass, '\\'); ?>;
use yii\web\HttpException;
use yii\web\VerbFilter;
/**
* <?php echo $controllerClass; ?> implements the CRUD actions for <?php echo $modelClass; ?> model.
*/
class <?php echo $controllerClass; ?> extends <?php echo StringHelper::basename($generator->baseControllerClass) . "\n"; ?>
{
public function behaviors()
{
return array(
'verbs' => array(
'class' => VerbFilter::className(),
'actions' => array(
'delete' => array('post'),
),
),
);
}
/**
* Lists all <?php echo $modelClass; ?> models.
* @return mixed
*/
public function actionIndex()
{
$searchModel = new <?php echo $searchModelClass; ?>;
$dataProvider = $searchModel->search($_GET);
return $this->render('index', array(
'dataProvider' => $dataProvider,
'searchModel' => $searchModel,
));
}
/**
* Displays a single <?php echo $modelClass; ?> model.
* <?php echo implode("\n\t * ", $actionParamComments) . "\n"; ?>
* @return mixed
*/
public function actionView(<?php echo $actionParams; ?>)
{
return $this->render('view', array(
'model' => $this->findModel(<?php echo $actionParams; ?>),
));
}
/**
* Creates a new <?php echo $modelClass; ?> model.
* If creation is successful, the browser will be redirected to the 'view' page.
* @return mixed
*/
public function actionCreate()
{
$model = new <?php echo $modelClass; ?>;
if ($model->load($_POST) && $model->save()) {
return $this->redirect(array('view', <?php echo $urlParams; ?>));
} else {
return $this->render('create', array(
'model' => $model,
));
}
}
/**
* Updates an existing <?php echo $modelClass; ?> model.
* If update is successful, the browser will be redirected to the 'view' page.
* <?php echo implode("\n\t * ", $actionParamComments) . "\n"; ?>
* @return mixed
*/
public function actionUpdate(<?php echo $actionParams; ?>)
{
$model = $this->findModel(<?php echo $actionParams; ?>);
if ($model->load($_POST) && $model->save()) {
return $this->redirect(array('view', <?php echo $urlParams; ?>));
} else {
return $this->render('update', array(
'model' => $model,
));
}
}
/**
* Deletes an existing <?php echo $modelClass; ?> model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* <?php echo implode("\n\t * ", $actionParamComments) . "\n"; ?>
* @return mixed
*/
public function actionDelete(<?php echo $actionParams; ?>)
{
$this->findModel(<?php echo $actionParams; ?>)->delete();
return $this->redirect(array('index'));
}
/**
* Finds the <?php echo $modelClass; ?> model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* <?php echo implode("\n\t * ", $actionParamComments) . "\n"; ?>
* @return <?php echo $modelClass; ?> the loaded model
* @throws HttpException if the model cannot be found
*/
protected function findModel(<?php echo $actionParams; ?>)
{
<?php
if (count($pks) === 1) {
$condition = '$id';
} else {
$condition = array();
foreach ($pks as $pk) {
$condition[] = "'$pk' => \$$pk";
}
$condition = 'array(' . implode(', ', $condition) . ')';
}
?>
if (($model = <?php echo $modelClass; ?>::find(<?php echo $condition; ?>)) !== null) {
return $model;
} else {
throw new HttpException(404, 'The requested page does not exist.');
}
}
}

8
framework/yii/gii/generators/crud/templates/model.php

@ -1,8 +0,0 @@
<?php
/**
* Created by JetBrains PhpStorm.
* User: qiang
* Date: 8/26/13
* Time: 5:22 PM
* To change this template use File | Settings | File Templates.
*/

83
framework/yii/gii/generators/crud/templates/search.php

@ -0,0 +1,83 @@
<?php
use yii\helpers\StringHelper;
/**
* This is the template for generating a CRUD controller class file.
*
* @var yii\base\View $this
* @var yii\gii\generators\crud\Generator $generator
*/
$modelClass = StringHelper::basename($generator->modelClass);
$searchModelClass = StringHelper::basename($generator->searchModelClass);
$rules = $generator->generateSearchRules();
$labels = $generator->generateSearchLabels();
$searchAttributes = $generator->getSearchAttributes();
$searchConditions = $generator->generateSearchConditions();
echo "<?php\n";
?>
namespace <?php echo StringHelper::dirname(ltrim($generator->searchModelClass, '\\')); ?>;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use <?php echo ltrim($generator->modelClass, '\\'); ?>;
/**
* <?php echo $searchModelClass; ?> represents the model behind the search form about <?php echo $modelClass; ?>.
*/
class <?php echo $searchModelClass; ?> extends Model
{
public $<?php echo implode(";\n\tpublic $", $searchAttributes); ?>;
public function rules()
{
return array(
<?php echo implode(",\n\t\t\t", $rules); ?>,
);
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return array(
<?php foreach ($labels as $name => $label): ?>
<?php echo "'$name' => '" . addslashes($label) . "',\n"; ?>
<?php endforeach; ?>
);
}
public function search($params)
{
$query = <?php echo $modelClass; ?>::find();
$dataProvider = new ActiveDataProvider(array(
'query' => $query,
));
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
<?php echo implode("\n\t\t", $searchConditions); ?>
return $dataProvider;
}
protected function addCondition($query, $attribute, $partialMatch = false)
{
$value = $this->$attribute;
if (trim($value) === '') {
return;
}
if ($partialMatch) {
$value = '%' . strtr($value, array('%'=>'\%', '_'=>'\_', '\\'=>'\\\\')) . '%';
$query->andWhere(array('like', $attribute, $value));
} else {
$query->andWhere(array($attribute => $value));
}
}
}

44
framework/yii/gii/generators/crud/templates/views/_form.php

@ -0,0 +1,44 @@
<?php
use yii\helpers\Inflector;
use yii\helpers\StringHelper;
/**
* @var yii\base\View $this
* @var yii\gii\generators\crud\Generator $generator
*/
/** @var \yii\db\ActiveRecord $model */
$model = new $generator->modelClass;
$safeAttributes = $model->safeAttributes();
if (empty($safeAttributes)) {
$safeAttributes = $model->getTableSchema()->columnNames;
}
echo "<?php\n";
?>
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/**
* @var yii\base\View $this
* @var <?php echo ltrim($generator->modelClass, '\\'); ?> $model
* @var yii\widgets\ActiveForm $form
*/
?>
<div class="<?php echo Inflector::camel2id(StringHelper::basename($generator->modelClass)); ?>-form">
<?php echo '<?php'; ?> $form = ActiveForm::begin(); ?>
<?php foreach ($safeAttributes as $attribute) {
echo "\t\t<?php echo " . $generator->generateActiveField($attribute) . " ?>\n\n";
} ?>
<div class="form-group">
<?php echo '<?php'; ?> echo Html::submitButton($model->isNewRecord ? 'Create' : 'Update', array('class' => 'btn btn-primary')); ?>
</div>
<?php echo '<?php'; ?> ActiveForm::end(); ?>
</div>

45
framework/yii/gii/generators/crud/templates/views/_search.php

@ -0,0 +1,45 @@
<?php
use yii\helpers\Inflector;
use yii\helpers\StringHelper;
/**
* @var yii\base\View $this
* @var yii\gii\generators\crud\Generator $generator
*/
echo "<?php\n";
?>
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/**
* @var yii\base\View $this
* @var <?php echo ltrim($generator->searchModelClass, '\\'); ?> $model
* @var yii\widgets\ActiveForm $form
*/
?>
<div class="<?php echo Inflector::camel2id(StringHelper::basename($generator->modelClass)); ?>-search">
<?php echo '<?php'; ?> $form = ActiveForm::begin(array('method' => 'get')); ?>
<?php
$count = 0;
foreach ($generator->getTableSchema()->getColumnNames() as $attribute) {
if (++$count < 6) {
echo "\t\t<?php echo " . $generator->generateActiveSearchField($attribute) . " ?>\n";
} else {
echo "\t\t<?php // echo " . $generator->generateActiveSearchField($attribute) . " ?>\n";
}
}
?>
<div class="form-group">
<?php echo '<?php'; ?> echo Html::submitButton('Search', array('class' => 'btn btn-primary')); ?>
<?php echo '<?php'; ?> echo Html::resetButton('Reset', array('class' => 'btn btn-default')); ?>
</div>
<?php echo '<?php'; ?> ActiveForm::end(); ?>
</div>

33
framework/yii/gii/generators/crud/templates/views/create.php

@ -0,0 +1,33 @@
<?php
use yii\helpers\Inflector;
use yii\helpers\StringHelper;
/**
* @var yii\base\View $this
* @var yii\gii\generators\crud\Generator $generator
*/
echo "<?php\n";
?>
use yii\helpers\Html;
/**
* @var yii\base\View $this
* @var <?php echo ltrim($generator->modelClass, '\\'); ?> $model
*/
$this->title = 'Create <?php echo Inflector::camel2words(StringHelper::basename($generator->modelClass)); ?>';
$this->params['breadcrumbs'][] = array('label' => '<?php echo Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass))); ?>', 'url' => array('index'));
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="<?php echo Inflector::camel2id(StringHelper::basename($generator->modelClass)); ?>-create">
<h1><?php echo "<?php"; ?> echo Html::encode($this->title); ?></h1>
<?php echo "<?php"; ?> echo $this->render('_form', array(
'model' => $model,
)); ?>
</div>

73
framework/yii/gii/generators/crud/templates/views/index.php

@ -0,0 +1,73 @@
<?php
use yii\helpers\Inflector;
use yii\helpers\StringHelper;
/**
* @var yii\base\View $this
* @var yii\gii\generators\crud\Generator $generator
*/
$urlParams = $generator->generateUrlParams();
$nameAttribute = $generator->getNameAttribute();
echo "<?php\n";
?>
use yii\helpers\Html;
use <?php echo $generator->indexWidgetType === 'grid' ? 'yii\grid\GridView' : 'yii\widgets\ListView'; ?>;
/**
* @var yii\base\View $this
* @var yii\data\ActiveDataProvider $dataProvider
* @var <?php echo ltrim($generator->searchModelClass, '\\'); ?> $searchModel
*/
$this->title = '<?php echo Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass))); ?>';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="<?php echo Inflector::camel2id(StringHelper::basename($generator->modelClass)); ?>-index">
<h1><?php echo "<?php"; ?> echo Html::encode($this->title); ?></h1>
<?php echo '<?php' . ($generator->indexWidgetType === 'grid' ? ' //' : ''); ?> echo $this->render('_search', array('model' => $searchModel)); ?>
<p>
<?php echo '<?php'; ?> echo Html::a('Create <?php echo StringHelper::basename($generator->modelClass); ?>', array('create'), array('class' => 'btn btn-danger')); ?>
</p>
<?php if ($generator->indexWidgetType === 'grid'): ?>
<?php echo "<?php"; ?> echo GridView::widget(array(
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => array(
array('class' => 'yii\grid\SerialColumn'),
<?php
$count = 0;
foreach ($generator->getTableSchema()->columns as $column) {
$format = $generator->generateColumnFormat($column);
if (++$count < 6) {
echo "\t\t\t'" . $column->name . ($format === 'text' ? '' : ':' . $format) . "',\n";
} else {
echo "\t\t\t// '" . $column->name . ($format === 'text' ? '' : ':' . $format) . "',\n";
}
}
?>
array('class' => 'yii\grid\ActionColumn'),
),
)); ?>
<?php else: ?>
<?php echo "<?php"; ?> echo ListView::widget(array(
'dataProvider' => $dataProvider,
'itemOptions' => array(
'class' => 'item',
),
'itemView' => function ($model, $key, $index, $widget) {
return Html::a(Html::encode($model-><?php echo $nameAttribute; ?>), array('view', <?php echo $urlParams; ?>));
},
)); ?>
<?php endif; ?>
</div>

36
framework/yii/gii/generators/crud/templates/views/update.php

@ -0,0 +1,36 @@
<?php
use yii\helpers\Inflector;
use yii\helpers\StringHelper;
/**
* @var yii\base\View $this
* @var yii\gii\generators\crud\Generator $generator
*/
$urlParams = $generator->generateUrlParams();
echo "<?php\n";
?>
use yii\helpers\Html;
/**
* @var yii\base\View $this
* @var <?php echo ltrim($generator->modelClass, '\\'); ?> $model
*/
$this->title = 'Update <?php echo Inflector::camel2words(StringHelper::basename($generator->modelClass)); ?>: ' . $model-><?php echo $generator->getNameAttribute(); ?>;
$this->params['breadcrumbs'][] = array('label' => '<?php echo Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass))); ?>', 'url' => array('index'));
$this->params['breadcrumbs'][] = array('label' => $model-><?php echo $generator->getNameAttribute(); ?>, 'url' => array('view', <?php echo $urlParams; ?>));
$this->params['breadcrumbs'][] = 'Update';
?>
<div class="<?php echo Inflector::camel2id(StringHelper::basename($generator->modelClass)); ?>-update">
<h1><?php echo "<?php"; ?> echo Html::encode($this->title); ?></h1>
<?php echo "<?php"; ?> echo $this->render('_form', array(
'model' => $model,
)); ?>
</div>

53
framework/yii/gii/generators/crud/templates/views/view.php

@ -0,0 +1,53 @@
<?php
use yii\helpers\Inflector;
use yii\helpers\StringHelper;
/**
* @var yii\base\View $this
* @var yii\gii\generators\crud\Generator $generator
*/
$urlParams = $generator->generateUrlParams();
echo "<?php\n";
?>
use yii\helpers\Html;
use yii\widgets\DetailView;
/**
* @var yii\base\View $this
* @var <?php echo ltrim($generator->modelClass, '\\'); ?> $model
*/
$this->title = $model-><?php echo $generator->getNameAttribute(); ?>;
$this->params['breadcrumbs'][] = array('label' => '<?php echo Inflector::pluralize(Inflector::camel2words(StringHelper::basename($generator->modelClass))); ?>', 'url' => array('index'));
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="<?php echo Inflector::camel2id(StringHelper::basename($generator->modelClass)); ?>-view">
<h1><?php echo "<?php"; ?> echo Html::encode($this->title); ?></h1>
<p>
<?php echo '<?php'; ?> echo Html::a('Update', array('update', <?php echo $urlParams; ?>), array('class' => 'btn btn-danger')); ?>
<?php echo '<?php'; ?> echo Html::a('Delete', array('delete', <?php echo $urlParams; ?>), array(
'class' => 'btn btn-danger',
'data-confirm' => Yii::t('app', 'Are you sure to delete this item?'),
'data-method' => 'post',
)); ?>
</p>
<?php echo '<?php'; ?> echo DetailView::widget(array(
'model' => $model,
'attributes' => array(
<?php
foreach ($generator->getTableSchema()->columns as $column) {
$format = $generator->generateColumnFormat($column);
echo "\t\t\t'" . $column->name . ($format === 'text' ? '' : ':' . $format) . "',\n";
}
?>
),
)); ?>
</div>

7
framework/yii/gii/generators/model/Generator.php

@ -93,8 +93,11 @@ class Generator extends \yii\gii\Generator
'db' => 'This is the ID of the DB application component.',
'tableName' => 'This is the name of the DB table that the new ActiveRecord class is associated with, e.g. <code>tbl_post</code>.
The table name may consist of the DB schema part if needed, e.g. <code>public.tbl_post</code>.
The table name may contain an asterisk at the end to match multiple table names, e.g. <code>tbl_*</code>.
In this case, multiple ActiveRecord classes will be generated, one for each matching table name.',
The table name may contain an asterisk to match multiple table names, e.g. <code>tbl_*</code>
will match tables who name starts with <code>tbl_</code>. In this case, multiple ActiveRecord classes
will be generated, one for each matching table name; and the class names will be generated from
the matching characters. For example, table <code>tbl_post</code> will generate <code>Post</code>
class.',
'modelClass' => 'This is the name of the ActiveRecord class to be generated. The class name should not contain
the namespace part as it is specified in "Namespace". You do not need to specify the class name
if "Table Name" contains an asterisk at the end, in which case multiple ActiveRecord classes will be generated.',

3
framework/yii/gii/views/default/view.php

@ -30,6 +30,7 @@ foreach ($generator->templates as $name => $path) {
<?php $form = ActiveForm::begin(array(
'id' => "$id-generator",
'successCssClass' => '',
'fieldConfig' => array('class' => ActiveField::className()),
)); ?>
<div class="row">
@ -39,7 +40,7 @@ foreach ($generator->templates as $name => $path) {
'form' => $form,
)); ?>
<?php echo $form->field($generator, 'template')->sticky()
->label(array('label' => 'Code Template'))
->label('Code Template')
->dropDownList($templates)->hint('
Please select which set of the templates should be used to generated the code.
'); ?>

102
framework/yii/grid/ActionColumn.php

@ -0,0 +1,102 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\grid;
use Yii;
use Closure;
use yii\helpers\Html;
use yii\helpers\Inflector;
use yii\helpers\StringHelper;
/**
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class ActionColumn extends Column
{
public $template = '{view} {update} {delete}';
public $buttons = array();
public $urlCreator;
public function init()
{
parent::init();
$this->initDefaultButtons();
}
protected function initDefaultButtons()
{
if (!isset($this->buttons['view'])) {
$this->buttons['view'] = function ($model, $column) {
/** @var ActionColumn $column */
$url = $column->createUrl($model, 'view');
return Html::a('<span class="glyphicon glyphicon-eye-open"></span>', $url, array(
'title' => Yii::t('yii', 'View'),
));
};
}
if (!isset($this->buttons['update'])) {
$this->buttons['update'] = function ($model, $column) {
/** @var ActionColumn $column */
$url = $column->createUrl($model, 'update');
return Html::a('<span class="glyphicon glyphicon-pencil"></span>', $url, array(
'title' => Yii::t('yii', 'Update'),
));
};
}
if (!isset($this->buttons['delete'])) {
$this->buttons['delete'] = function ($model, $column) {
/** @var ActionColumn $column */
$url = $column->createUrl($model, 'delete');
return Html::a('<span class="glyphicon glyphicon-trash"></span>', $url, array(
'title' => Yii::t('yii', 'Delete'),
'data-confirm' => Yii::t('yii', 'Are you sure to delete this item?'),
'data-method' => 'post',
));
};
}
}
/**
* @param \yii\db\ActiveRecord $model
* @param string $action
* @return string
*/
public function createUrl($model, $action)
{
if ($this->urlCreator instanceof Closure) {
return call_user_func($this->urlCreator, $model, $action);
} else {
$route = Inflector::camel2id(StringHelper::basename(get_class($model))) . '/' . $action;
$params = $model->getPrimaryKey(true);
if (count($params) === 1) {
$params = array('id' => reset($params));
}
return Yii::$app->getUrlManager()->createUrl($route, $params);
}
}
/**
* Renders the data cell content.
* @param mixed $model the data model
* @param integer $index the zero-based index of the data model among the models array returned by [[dataProvider]].
* @return string the rendering result
*/
protected function renderDataCellContent($model, $index)
{
$column = $this;
return preg_replace_callback('/\\{(\w+)\\}/', function ($matches) use ($model, $column) {
$name = $matches[1];
if (isset($column->buttons[$name])) {
return call_user_func($column->buttons[$name], $model, $column);
} else {
return '';
}
}, $this->template);
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save