Commit 16724d2f by Thiago Talma

Merge branch 'master' of https://github.com/yiisoft/yii2

parents df66c41c dc4feef7
<?php
return [
'preload' => [
'debug',
],
'modules' => [
'debug' => 'yii\debug\Module',
'gii' => 'yii\gii\Module',
],
];
<?php
return [
'preload' => [
//'debug',
],
'modules' => [
// 'debug' => 'yii\debug\Module',
// 'gii' => 'yii\gii\Module',
],
'components' => [
'db' => [
'class' => 'yii\db\Connection',
......
<?php
return [
'preload' => [
'debug',
],
'modules' => [
'debug' => 'yii\debug\Module',
'gii' => 'yii\gii\Module',
],
];
......@@ -271,6 +271,12 @@ whose subtotal is greater than 100. To specify a different threshold value, use
$orders = $customer->getBigOrders(200)->all();
```
> Note: A relation method returns an instance of [[yii\db\ActiveRelation]]. If you access the relation like
an attribute, the return value will be the query result of the relation, which could be an instance of `ActiveRecord`,
an array of that, or null, depending the multiplicity of the relation. For example, `$customer->getOrders()` returns
an `ActiveRelation` instance, while `$customer->orders` returns an array of `Order` objects (or an empty array if
the query results in nothing).
Relations with Pivot Table
--------------------------
......@@ -543,16 +549,19 @@ Finally when calling [[yii\db\ActiveRecord::delete()|delete()]] to delete an Act
3. [[yii\db\ActiveRecord::afterDelete()|afterDelete()]]: will trigger an [[yii\db\ActiveRecord::EVENT_AFTER_DELETE|EVENT_AFTER_DELETE]] event
Custom scopes
-------------
Scopes
------
When [[yii\db\ActiveRecord::find()|find()]] or [[yii\db\ActiveRecord::findBySql()|findBySql()]], it returns an [[yii\db\ActiveRecord::yii\db\ActiveQuery|yii\db\ActiveQuery]]
instance. You may call additional query methods, such as `where()`, `orderBy()`, to further specify the query conditions, etc.
When [[yii\db\ActiveRecord::find()|find()]] or [[yii\db\ActiveRecord::findBySql()|findBySql()]] Active Record method is being called without parameters it returns an [[yii\db\ActiveRecord::yii\db\ActiveQuery|yii\db\ActiveQuery]]
instance. This object holds all the parameters and conditions for a future query and also allows you to customize these
using a set of methods that are called scopes. By default there is a good set of such methods some of which we've
already used above: `where`, `orderBy`, `limit` etc.
It is possible that you may want to call the same set of query methods in different places. If this is the case,
you should consider defining the so-called *scopes*. A scope is essentially a method defined in a custom query class that
calls a set of query methods to modify the query object. You can then use a scope like calling a normal query method.
In many cases it is convenient to wrap extra conditions into custom scope methods. In order to do so you need two things.
First is creating a custom query class for your model. For example, a `Comment` may have a `CommentQuery`:
Two steps are required to define a scope. First create a custom query class for your model and define the needed scope
methods in this class. For example, create a `CommentQuery` class for the `Comment` model and define the `active()`
scope method like the following:
```php
namespace app\models;
......@@ -575,7 +584,8 @@ Important points are:
2. A method should be `public` and should return `$this` in order to allow method chaining. It may accept parameters.
3. Check `ActiveQuery` methods that are very useful for modifying query conditions.
The second step is to use `CommentQuery` instead of regular `ActiveQuery` for `Comment` model:
Second, override `ActiveRecord::createQuery()` to use the custom query class instead of the regular `ActiveQuery`.
For the example above, you need to write the following code:
```
namespace app\models;
......@@ -621,6 +631,21 @@ $posts = Post::find()->with([
])->all();
```
### Default Scope
If you used Yii 1.1 before, you may know a concept called *default scope*. A default scope is a scope that
applies to ALL queries. You can define a default scope easily by overriding `ActiveRecord::createQuery()`. For example,
```php
public static function createQuery()
{
$query = new CommentQuery(['modelClass' => get_called_class()]);
$query->where(['deleted' => false]);
return $query;
}
```
### Making it IDE-friendly
In order to make most modern IDE autocomplete happy you need to override return types for some methods of both model
......
......@@ -91,7 +91,7 @@ If controller is located inside a module its action internal route will be `modu
In case module, controller or action specified isn't found Yii will return "not found" page and HTTP status code 404.
> Note: If controller name or action name contains camelCased words, internal route will use dashes i.e. for
> Note: If module name, controller name or action name contains camelCased words, internal route will use dashes i.e. for
`DateTimeController::actionFastForward` route will be `date-time/fast-forward`.
### Defaults
......
......@@ -78,7 +78,7 @@ Yii tries to load appropriate translation from one of the message sources define
```
In the above `app*` is a pattern that specifies which categories are handled by the message source. In this case we're
handling everything that begins with `app`. You can also specify default translation, for more info see [this](specifying-default-translation) example.
handling everything that begins with `app`. You can also specify default translation, for more info see [this example](i18n.md#examples).
`class` defines which message source is used. The following message sources are available:
......@@ -356,7 +356,8 @@ class Module extends \yii\base\Module
}
```
In the example above we are using wildcard for matching and then filtering each category per needed file.
In the example above we are using wildcard for matching and then filtering each category per needed file. Instead of using `fileMap` you can simply
use convention of category mapping to the same named file and use `Module::t('validation', 'your custom validation message')` or `Module::t('form', 'some form label')` directly.
###Translating widgets messages
......@@ -405,6 +406,8 @@ class Menu extends Widget
}
```
Instead of using `fileMap` you can simply use convention of category mapping to the same named file and use `Menu::t('messages', 'new messages {messages}', ['{messages}' => 10])` directly.
> **Note**: For widgets you also can use i18n views, same rules as for controllers are applied to them too.
TBD: provided classes overview.
......@@ -232,6 +232,7 @@ abstract class Renderer extends BaseRenderer implements ViewContextInterface
*/
public function renderInheritance($class)
{
$parents = [];
$parents[] = $this->typeLink($class);
while ($class->parentClass !== null) {
if(isset($this->context->classes[$class->parentClass])) {
......
......@@ -9,6 +9,8 @@ namespace yii\debug;
use yii\web\AssetBundle;
/**
* Debugger asset bundle
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
......
......@@ -72,6 +72,13 @@ class LogTarget extends Target
$this->updateIndexFile($indexFile, $summary);
}
/**
* Updates index file with summary log data
*
* @param string $indexFile path to index file
* @param array $summary summary log data
* @throws \yii\base\InvalidConfigException
*/
private function updateIndexFile($indexFile, $summary)
{
touch($indexFile);
......
......@@ -50,7 +50,9 @@ class Module extends \yii\base\Module
*/
public $historySize = 50;
/**
* @inheritdoc
*/
public function init()
{
parent::init();
......@@ -69,13 +71,16 @@ class Module extends \yii\base\Module
}
}
/**
* @inheritdoc
*/
public function beforeAction($action)
{
Yii::$app->getView()->off(View::EVENT_END_BODY, [$this, 'renderToolbar']);
unset(Yii::$app->getLog()->targets['debug']);
$this->logTarget = null;
if ($this->checkAccess($action)) {
if ($this->checkAccess()) {
return parent::beforeAction($action);
} elseif ($action->id === 'toolbar') {
return false;
......@@ -84,6 +89,11 @@ class Module extends \yii\base\Module
}
}
/**
* Renders mini-toolbar at the end of page body.
*
* @param \yii\base\Event $event
*/
public function renderToolbar($event)
{
if (!$this->checkAccess() || Yii::$app->getRequest()->getIsAjax()) {
......@@ -99,6 +109,10 @@ class Module extends \yii\base\Module
echo '<script>' . $view->renderPhpFile(__DIR__ . '/assets/toolbar.js') . '</script>';
}
/**
* Checks if current user is allowed to access the module
* @return boolean if access is granted
*/
protected function checkAccess()
{
$ip = Yii::$app->getRequest()->getUserIP();
......@@ -111,6 +125,9 @@ class Module extends \yii\base\Module
return false;
}
/**
* @return array default set of panels
*/
protected function corePanels()
{
return [
......
......@@ -73,6 +73,11 @@ class Panel extends Component
return null;
}
/**
* Loads data into the panel
*
* @param mixed $data
*/
public function load($data)
{
$this->data = $data;
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\components\search;
use yii\base\Component;
use yii\debug\components\search\matchers\MatcherInterface;
/**
* Provides array filtering capabilities.
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class Filter extends Component
{
/**
* @var array rules for matching filters in the way: [:fieldName => [rule1, rule2,..]]
*/
protected $rules = [];
/**
* Adds rules for filtering data. Match can be partial or exactly.
* Adds data filtering rule.
*
* @param string $name attribute name
* @param \yii\debug\components\search\matches\Base $rule
* @param MatcherInterface $rule
*/
public function addMatch($name, $rule)
public function addMatcher($name, MatcherInterface $rule)
{
if (empty($rule->value) && $rule->value !== 0) {
return;
if ($rule->hasValue()) {
$this->rules[$name][] = $rule;
}
$this->rules[$name][] = $rule;
}
/**
* Applies filter on given array and returns filtered data.
* Applies filter on a given array and returns filtered data.
*
* @param array $data data to filter
* @return array filtered data
*/
......@@ -36,7 +47,7 @@ class Filter extends Component
$filtered = [];
foreach ($data as $row) {
if ($this->checkFilter($row)) {
if ($this->passesFilter($row)) {
$filtered[] = $row;
}
}
......@@ -45,28 +56,25 @@ class Filter extends Component
}
/**
* Check if the given data satisfies filters.
* @param array $row
* Checks if the given data satisfies filters.
*
* @param array $row data
* @return boolean if data passed filtering
*/
public function checkFilter(array $row)
private function passesFilter(array $row)
{
$matched = true;
foreach ($row as $name => $value) {
if (isset($this->rules[$name])) {
#check all rules for given attribute
// check all rules for a given attribute
foreach ($this->rules[$name] as $rule) {
if (!$rule->check($value)) {
$matched = false;
/** @var MatcherInterface $rule */
if (!$rule->match($value)) {
return false;
}
}
}
}
return $matched;
return true;
}
}
......@@ -5,22 +5,36 @@
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\components\search\matches;
namespace yii\debug\components\search\matchers;
use yii\base\Component;
/**
* Base mathcer class for all matchers that will be used with filter.
* Base class for matchers that are used in a filter.
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
abstract class Base extends Component implements MatcherInterface
{
/**
* @var mixed base value to check
*/
protected $baseValue;
/**
* @var mixed current value to check for the matcher
* @inheritdoc
*/
public $value;
public function setValue($value)
{
$this->baseValue = $value;
}
/**
* @inheritdoc
*/
public function hasValue()
{
return !empty($this->baseValue) || $this->baseValue === 0;
}
}
......@@ -5,23 +5,21 @@
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\components\search\matches;
namespace yii\debug\components\search\matchers;
/**
* Checks if the given value is greater than the base one.
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class Greater extends Base
class GreaterThan extends Base
{
/**
* Checks if the given value is the same as base one or has partial match with base one.
* @param mixed $value
* @inheritdoc
*/
public function check($value)
public function match($value)
{
return ($value > $this->value);
return ($value > $this->baseValue);
}
}
......@@ -5,23 +5,21 @@
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\components\search\matches;
namespace yii\debug\components\search\matchers;
/**
* Checks if the given value is lower than the base one.
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class Lower extends Base
class LowerThan extends Base
{
/**
* Checks if the given value is the same as base one or has partial match with base one.
* @param mixed $value
* @inheritdoc
*/
public function check($value)
public function match($value)
{
return ($value < $this->value);
return ($value < $this->baseValue);
}
}
......@@ -5,21 +5,35 @@
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\components\search\matches;
namespace yii\debug\components\search\matchers;
/**
* MatcherInterface is the interface that should be implemented by all matchers that will be used in filter.
* MatcherInterface should be implemented by all matchers that are used in a filter.
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
interface MatcherInterface
{
/**
* Checks if the value passed matches base value.
*
* @param mixed $value value to be matched
* @return boolean if there is a match
*/
public function match($value);
/**
* Check if the value is correct according current matcher.
* Sets base value to match against
*
* @param mixed $value
*/
public function check($value);
public function setValue($value);
/**
* Checks if base value is set
*
* @return boolean if base value is set
*/
public function hasValue();
}
......@@ -5,32 +5,30 @@
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\components\search\matches;
namespace yii\debug\components\search\matchers;
/**
* Checks if the given value is exactly or partially same as the base one.
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class Exact extends Base
class SameAs extends Base
{
/**
* @var boolean if current matcher should consider partial match of given value.
* @var boolean if partial match should be used.
*/
public $partial = false;
/**
* Checks if the given value is the same as base one or has partial match with base one.
* @param mixed $value
* @inheritdoc
*/
public function check($value)
public function match($value)
{
if (!$this->partial) {
return (mb_strtolower($this->value, 'utf8') == mb_strtolower($value, 'utf8'));
return (mb_strtolower($this->baseValue, 'utf8') == mb_strtolower($value, 'utf8'));
} else {
return (mb_strpos(mb_strtolower($value, 'utf8'), mb_strtolower($this->value,'utf8')) !== false);
return (mb_strpos(mb_strtolower($value, 'utf8'), mb_strtolower($this->baseValue, 'utf8')) !== false);
}
}
}
......@@ -13,11 +13,16 @@ use yii\web\NotFoundHttpException;
use yii\debug\models\search\Debug;
/**
* Debugger controller
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class DefaultController extends Controller
{
/**
* @inheritdoc
*/
public $layout = 'main';
/**
* @var \yii\debug\Module
......@@ -28,6 +33,9 @@ class DefaultController extends Controller
*/
public $summary;
/**
* @inheritdoc
*/
public function actions()
{
$actions = [];
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\models\search;
use yii\base\Model;
use yii\debug\components\search\Filter;
use yii\debug\components\search\matches;
use yii\debug\components\search\matchers;
/**
* Base search model
*
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class Base extends Model
{
/**
* @param Filter $filter
* @param string $attribute
* @param boolean $partial
* Adds filtering condition for a given attribute
*
* @param Filter $filter filter instance
* @param string $attribute attribute to filter
* @param boolean $partial if partial match should be used
*/
public function addCondition($filter, $attribute, $partial = false)
public function addCondition(Filter $filter, $attribute, $partial = false)
{
$value = $this->$attribute;
if (mb_strpos($value, '>') !== false) {
$value = intval(str_replace('>', '', $value));
$filter->addMatch($attribute, new matches\Greater(['value' => $value]));
$filter->addMatcher($attribute, new matchers\GreaterThan(['value' => $value]));
} elseif (mb_strpos($value, '<') !== false) {
$value = intval(str_replace('<', '', $value));
$filter->addMatch($attribute, new matches\Lower(['value' => $value]));
$filter->addMatcher($attribute, new matchers\LowerThan(['value' => $value]));
} else {
$filter->addMatch($attribute, new matches\Exact(['value' => $value, 'partial' => $partial]));
$filter->addMatcher($attribute, new matchers\SameAs(['value' => $value, 'partial' => $partial]));
}
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\models\search;
......@@ -6,13 +11,16 @@ use yii\data\ArrayDataProvider;
use yii\debug\components\search\Filter;
/**
* Db represents the model behind the search form about current request database queries.
* Search model for current request database queries.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class Db extends Base
{
/**
* @var string type attribute input search value
* @var string type of the input search value
*/
public $type;
......@@ -21,6 +29,9 @@ class Db extends Base
*/
public $query;
/**
* @inheritdoc
*/
public function rules()
{
return [
......@@ -41,8 +52,9 @@ class Db extends Base
/**
* Returns data provider with filled models. Filter applied if needed.
* @param array $params
* @param array $models
*
* @param array $params an array of parameter values indexed by parameter names
* @param array $models data to return provider for
* @return \yii\data\ArrayDataProvider
*/
public function search($params, $models)
......@@ -69,5 +81,4 @@ class Db extends Base
return $dataProvider;
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\models\search;
......@@ -6,7 +11,11 @@ use yii\data\ArrayDataProvider;
use yii\debug\components\search\Filter;
/**
* Debug represents the model behind the search form about requests manifest data.
* Search model for requests manifest data.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class Debug extends Base
{
......@@ -51,6 +60,9 @@ class Debug extends Base
*/
public $criticalCodes = [400, 404, 500];
/**
* @inheritdoc
*/
public function rules()
{
return [
......@@ -76,8 +88,8 @@ class Debug extends Base
/**
* Returns data provider with filled models. Filter applied if needed.
* @param array $params
* @param array $models
* @param array $params an array of parameter values indexed by parameter names
* @param array $models data to return provider for
* @return \yii\data\ArrayDataProvider
*/
public function search($params, $models)
......@@ -110,13 +122,13 @@ class Debug extends Base
}
/**
* Checks if the code is critical: 400 or greater, 500 or greater.
* Checks if code is critical.
*
* @param integer $code
* @return bool
* @return boolean
*/
public function isCodeCritical($code)
{
return in_array($code, $this->criticalCodes);
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\models\search;
......@@ -6,11 +11,14 @@ use yii\data\ArrayDataProvider;
use yii\debug\components\search\Filter;
/**
* Log represents the model behind the search form about current request log.
* Search model for current request log.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class Log extends Base
{
/**
* @var string ip attribute input search value
*/
......@@ -26,6 +34,9 @@ class Log extends Base
*/
public $message;
/**
* @inheritdoc
*/
public function rules()
{
return [
......@@ -47,8 +58,9 @@ class Log extends Base
/**
* Returns data provider with filled models. Filter applied if needed.
* @param array $params
* @param array $models
*
* @param array $params an array of parameter values indexed by parameter names
* @param array $models data to return provider for
* @return \yii\data\ArrayDataProvider
*/
public function search($params, $models)
......@@ -73,5 +85,4 @@ class Log extends Base
return $dataProvider;
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\debug\models\search;
......@@ -6,11 +11,14 @@ use yii\data\ArrayDataProvider;
use yii\debug\components\search\Filter;
/**
* Profile represents the model behind the search form about current request profiling log.
* Search model for current request profiling log.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Mark Jebri <mark.github@yandex.ru>
* @since 2.0
*/
class Profile extends Base
{
/**
* @var string method attribute input search value
*/
......@@ -21,6 +29,9 @@ class Profile extends Base
*/
public $info;
/**
* @inheritdoc
*/
public function rules()
{
return [
......@@ -41,8 +52,9 @@ class Profile extends Base
/**
* Returns data provider with filled models. Filter applied if needed.
* @param array $params
* @param array $models
*
* @param array $params an array of parameter values indexed by parameter names
* @param array $models data to return provider for
* @return \yii\data\ArrayDataProvider
*/
public function search($params, $models)
......@@ -69,5 +81,4 @@ class Profile extends Base
return $dataProvider;
}
}
......@@ -18,26 +18,45 @@ use yii\debug\Panel;
*/
class ConfigPanel extends Panel
{
/**
* @inheritdoc
*/
public function getName()
{
return 'Configuration';
}
/**
* Returns Yii logo ready to use in `<img src="`
*
* @return string base64 representation of the image
*/
public static function getYiiLogo()
{
return 'data:image/png;base64,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';
}
/**
* @inheritdoc
*/
public function getSummary()
{
return Yii::$app->view->render('panels/config/summary', ['panel' => $this]);
}
/**
* @inheritdoc
*/
public function getDetail()
{
return Yii::$app->view->render('panels/config/detail', ['panel' => $this]);
}
/**
* Returns data about extensions
*
* @return array
*/
public function getExtensions()
{
$data = [];
......@@ -47,6 +66,9 @@ class ConfigPanel extends Panel
return $data;
}
/**
* @inheritdoc
*/
public function save()
{
return [
......
......@@ -20,7 +20,6 @@ use yii\debug\models\search\Db;
*/
class DbPanel extends Panel
{
/**
* @var array db queries info extracted to array as models, to use with data provider.
*/
......@@ -31,11 +30,17 @@ class DbPanel extends Panel
*/
private $_timings;
/**
* @inheritdoc
*/
public function getName()
{
return 'Database';
}
/**
* @inheritdoc
*/
public function getSummary()
{
$timings = $this->calculateTimings();
......@@ -50,6 +55,9 @@ class DbPanel extends Panel
]);
}
/**
* @inheritdoc
*/
public function getDetail()
{
$searchModel = new Db();
......@@ -63,7 +71,8 @@ class DbPanel extends Panel
}
/**
* Calculates given request profile messages timings.
* Calculates given request profile timings.
*
* @return array timings [token, category, timestamp, traces, nesting level, elapsed time]
*/
protected function calculateTimings()
......@@ -74,6 +83,9 @@ class DbPanel extends Panel
return $this->_timings;
}
/**
* @inheritdoc
*/
public function save()
{
$target = $this->module->logTarget;
......@@ -82,7 +94,8 @@ class DbPanel extends Panel
}
/**
* Returns total queries time.
* Returns total query time.
*
* @param array $timings
* @return integer total time
*/
......@@ -98,8 +111,8 @@ class DbPanel extends Panel
}
/**
* Returns array of models that represents logs of the current request. Can be used with data providers,
* like yii\data\ArrayDataProvider.
* Returns an array of models that represents logs of the current request.
* Can be used with data providers such as \yii\data\ArrayDataProvider.
* @return array models
*/
protected function getModels()
......@@ -110,7 +123,7 @@ class DbPanel extends Panel
foreach($timings as $seq => $dbTiming) {
$this->_models[] = [
'type' => $this->detectQueryType($dbTiming['info']),
'type' => $this->getQueryType($dbTiming['info']),
'query' => $dbTiming['info'],
'duration' => ($dbTiming['duration'] * 1000), // in milliseconds
'trace' => $dbTiming['trace'],
......@@ -123,16 +136,15 @@ class DbPanel extends Panel
}
/**
* Detects databse timing type. Detecting is produced through simple parsing to the first space|tab|new row.
* First word before space is timing type. If there is no such words, timing will have empty type.
* Returns databse query type.
*
* @param string $timing timing procedure string
* @return string query type select|insert|delete|etc
* @return string query type such as select, insert, delete, etc.
*/
protected function detectQueryType($timing)
protected function getQueryType($timing)
{
$timing = ltrim($timing);
preg_match('/^([a-zA-z]*)/', $timing, $matches);
return count($matches) ? $matches[0] : '';
}
}
......@@ -20,22 +20,30 @@ use yii\debug\models\search\Log;
*/
class LogPanel extends Panel
{
/**
* @var array log messages extracted to array as models, to use with data provider.
*/
private $_models;
/**
* @inheritdoc
*/
public function getName()
{
return 'Logs';
}
/**
* @inheritdoc
*/
public function getSummary()
{
return Yii::$app->view->render('panels/log/summary', ['data' => $this->data, 'panel' => $this]);
}
/**
* @inheritdoc
*/
public function getDetail()
{
$searchModel = new Log();
......@@ -48,6 +56,9 @@ class LogPanel extends Panel
]);
}
/**
* @inheritdoc
*/
public function save()
{
$target = $this->module->logTarget;
......@@ -56,9 +67,10 @@ class LogPanel extends Panel
}
/**
* Returns array of models that represents logs of the current request. Can be used with data providers,
* like yii\data\ArrayDataProvider.
* @param boolean $refresh if needed to build models from log messages and refresh them.
* Returns an array of models that represents logs of the current request.
* Can be used with data providers, such as \yii\data\ArrayDataProvider.
*
* @param boolean $refresh if need to build models from log messages and refresh them.
* @return array models
*/
protected function getModels($refresh = false)
......@@ -78,5 +90,4 @@ class LogPanel extends Panel
}
return $this->_models;
}
}
......@@ -25,11 +25,17 @@ class ProfilingPanel extends Panel
*/
private $_models;
/**
* @inheritdoc
*/
public function getName()
{
return 'Profiling';
}
/**
* @inheritdoc
*/
public function getSummary()
{
return Yii::$app->view->render('panels/profile/summary', [
......@@ -39,6 +45,9 @@ class ProfilingPanel extends Panel
]);
}
/**
* @inheritdoc
*/
public function getDetail()
{
$searchModel = new Profile();
......@@ -53,6 +62,9 @@ class ProfilingPanel extends Panel
]);
}
/**
* @inheritdoc
*/
public function save()
{
$target = $this->module->logTarget;
......@@ -65,7 +77,7 @@ class ProfilingPanel extends Panel
}
/**
* Returns array of profiling models that can be used in data provider.
* Returns array of profiling models that can be used in a data provider.
* @return array models
*/
protected function getModels()
......@@ -87,5 +99,4 @@ class ProfilingPanel extends Panel
}
return $this->_models;
}
}
......@@ -19,24 +19,37 @@ use yii\debug\Panel;
*/
class RequestPanel extends Panel
{
/**
* @inheritdoc
*/
public function getName()
{
return 'Request';
}
/**
* @inheritdoc
*/
public function getSummary()
{
return Yii::$app->view->render('panels/request/summary', ['panel' => $this]);
}
/**
* @inheritdoc
*/
public function getDetail()
{
return Yii::$app->view->render('panels/request/detail', ['panel' => $this]);
}
/**
* @inheritdoc
*/
public function save()
{
$headers = Yii::$app->getRequest()->getHeaders();
$requestHeaders = [];
foreach ($headers as $name => $value) {
if (is_array($value) && count($value) == 1) {
$requestHeaders[$name] = current($value);
......@@ -95,5 +108,4 @@ class RequestPanel extends Panel
'SESSION' => empty($_SESSION) ? [] : $_SESSION,
];
}
}
<?php
use yii\helpers\Html;
use yii\grid\GridView;
use yii\data\ArrayDataProvider;
use yii\log\Logger;
?>
<h1>Log Messages</h1>
......
......@@ -33,4 +33,3 @@ echo Tabs::widget([
],
],
]);
?>
......@@ -165,8 +165,7 @@ class Query extends Component implements QueryInterface
*/
public function one($db = null)
{
$options['size'] = 1;
$result = $this->createCommand($db)->search($options);
$result = $this->createCommand($db)->search(['size' => 1]);
if (empty($result['hits']['hits'])) {
return false;
}
......
......@@ -123,6 +123,11 @@ class CodeFile extends Object
}
}
/**
* Returns preview or false if it cannot be rendered
*
* @return boolean|string
*/
public function preview()
{
if (($pos = strrpos($this->path, '.')) !== false) {
......@@ -140,6 +145,11 @@ class CodeFile extends Object
}
}
/**
* Returns diff or false if it cannot be calculated
*
* @return boolean|string
*/
public function diff()
{
$type = strtolower($this->getType());
......@@ -152,6 +162,13 @@ class CodeFile extends Object
}
}
/**
* Renders diff between two sets of lines
*
* @param mixed $lines1
* @param mixed $lines2
* @return string
*/
private function renderDiff($lines1, $lines2)
{
if (!is_array($lines1)) {
......
......@@ -190,7 +190,6 @@ abstract class Generator extends Model
public function loadStickyAttributes()
{
$stickyAttributes = $this->stickyAttributes();
$attributes[] = 'template';
$path = $this->getStickyDataFile();
if (is_file($path)) {
$result = json_decode(file_get_contents($path), true);
......
......@@ -21,6 +21,9 @@ class ActiveField extends \yii\widgets\ActiveField
*/
public $model;
/**
* @inheritdoc
*/
public function init()
{
$stickyAttributes = $this->model->stickyAttributes();
......
......@@ -107,6 +107,9 @@ class DefaultController extends Controller
}
}
/**
* @inheritdoc
*/
public function createUrl($route, $params = [])
{
if (!isset($params['id']) && $this->generator !== null) {
......@@ -120,6 +123,13 @@ class DefaultController extends Controller
return parent::createUrl($route, $params);
}
/**
* Creates URL for an aciton
*
* @param string $name name of the action
* @param array $params the parameters (name-value pairs) to be included in the generated URL
* @return string the created relative URL
*/
public function createActionUrl($name, $params = [])
{
foreach ($this->module->generators as $id => $generator) {
......
......@@ -33,17 +33,26 @@ class Generator extends \yii\gii\Generator
public $indexWidgetType = 'grid';
public $searchModelClass;
/**
* @inheritdoc
*/
public function getName()
{
return 'CRUD Generator';
}
/**
* @inheritdoc
*/
public function getDescription()
{
return 'This generator generates a controller and views that implement CRUD (Create, Read, Update, Delete)
operations for the specified data model.';
}
/**
* @inheritdoc
*/
public function rules()
{
return array_merge(parent::rules(), [
......@@ -61,6 +70,9 @@ class Generator extends \yii\gii\Generator
]);
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return array_merge(parent::attributeLabels(), [
......@@ -94,6 +106,9 @@ class Generator extends \yii\gii\Generator
];
}
/**
* @inheritdoc
*/
public function requiredTemplates()
{
return ['controller.php'];
......@@ -107,6 +122,9 @@ class Generator extends \yii\gii\Generator
return ['baseControllerClass', 'moduleID', 'indexWidgetType'];
}
/**
* Checks if model class is valid
*/
public function validateModelClass()
{
/** @var ActiveRecord $class */
......@@ -117,6 +135,9 @@ class Generator extends \yii\gii\Generator
}
}
/**
* Checks if model ID is valid
*/
public function validateModuleID()
{
if (!empty($this->moduleID)) {
......@@ -184,6 +205,7 @@ class Generator extends \yii\gii\Generator
}
/**
* Generates code for active field
* @param string $attribute
* @return string
*/
......@@ -217,6 +239,7 @@ class Generator extends \yii\gii\Generator
}
/**
* Generates code for active search field
* @param string $attribute
* @return string
*/
......@@ -235,6 +258,7 @@ class Generator extends \yii\gii\Generator
}
/**
* Generates column format
* @param \yii\db\ColumnSchema $column
* @return string
*/
......@@ -298,6 +322,9 @@ class Generator extends \yii\gii\Generator
return $rules;
}
/**
* @return array searchable attributes
*/
public function getSearchAttributes()
{
return $this->getColumnNames();
......@@ -309,6 +336,7 @@ class Generator extends \yii\gii\Generator
*/
public function generateSearchLabels()
{
/** @var \yii\base\Model $model */
$model = new $this->modelClass();
$attributeLabels = $model->attributeLabels();
$labels = [];
......@@ -330,11 +358,16 @@ class Generator extends \yii\gii\Generator
return $labels;
}
/**
* Generates search conditions
* @return array
*/
public function generateSearchConditions()
{
$columns = [];
if (($table = $this->getTableSchema()) === false) {
$class = $this->modelClass;
/** @var \yii\base\Model $model */
$model = new $class();
foreach ($model->attributes() as $attribute) {
$columns[$attribute] = 'unknown';
......@@ -369,6 +402,10 @@ class Generator extends \yii\gii\Generator
return $conditions;
}
/**
* Generates URL parameters
* @return string
*/
public function generateUrlParams()
{
/** @var ActiveRecord $class */
......@@ -385,6 +422,10 @@ class Generator extends \yii\gii\Generator
}
}
/**
* Generates action parameters
* @return string
*/
public function generateActionParams()
{
/** @var ActiveRecord $class */
......@@ -397,6 +438,10 @@ class Generator extends \yii\gii\Generator
}
}
/**
* Generates parameter tags for phpdoc
* @return array parameter tags for phpdoc
*/
public function generateActionParamComments()
{
/** @var ActiveRecord $class */
......@@ -420,6 +465,10 @@ class Generator extends \yii\gii\Generator
}
}
/**
* Returns table schema for current model class or false if it is not an active record
* @return boolean|\yii\db\TableSchema
*/
public function getTableSchema()
{
/** @var ActiveRecord $class */
......@@ -431,6 +480,9 @@ class Generator extends \yii\gii\Generator
}
}
/**
* @return array model column names
*/
public function getColumnNames()
{
/** @var ActiveRecord $class */
......@@ -438,6 +490,7 @@ class Generator extends \yii\gii\Generator
if (is_subclass_of($class, 'yii\db\ActiveRecord')) {
return $class::getTableSchema()->getColumnNames();
} else {
/** @var \yii\base\Model $model */
$model = new $class();
return $model->attributes();
}
......
......@@ -155,7 +155,7 @@ class BaseImage
$img = $img->thumbnail($box, $mode);
// create empty image to preserve aspect ratio of thumbnail
$thumb = static::getImagine()->create($box);
$thumb = static::getImagine()->create($box, new Color('FFF', 100));
// calculate points
$size = $img->getSize();
......
......@@ -6,6 +6,7 @@ Yii Framework 2 Change Log
- Bug #1265: AssetController does not override 'js' and 'css' for compressed bundles (klimov-paul)
- Bug #1326: The `visible` setting for `DetailView` doesn't work as expected (qiangxue)
- Bug #1412: `FileValidator` and `ImageValidator` still trigger `uploadRequired` error in some case when `skipOnEmpty` is true and no upload is provided (qiangxue)
- Bug #1446: Logging while logs are processed causes infinite loop (qiangxue)
- Bug #1497: Localized view files are not correctly returned (mintao)
- Bug #1500: Log messages exported to files are not separated by newlines (omnilight, qiangxue)
......@@ -114,6 +115,7 @@ Yii Framework 2 Change Log
- Enh: Added `yii\web\View::POS_LOAD` (qiangxue)
- Enh: Added `yii\web\Response::clearOutputBuffers()` (qiangxue)
- Enh: Improved `QueryBuilder::buildLimit()` to support big numbers (qiangxue)
- Enh:#2211: Added typecast database types into php types (dizews)
- Enh #2240: Improved `yii\web\AssetManager::publish()`, `yii\web\AssetManager::getPublishedPath()` and `yii\web\AssetManager::getPublishedUrl()` to support aliases (vova07)
- Chg #1519: `yii\web\User::loginRequired()` now returns the `Response` object instead of exiting the application (qiangxue)
- Chg #1586: `QueryBuilder::buildLikeCondition()` will now escape special characters and use percentage characters by default (qiangxue)
......@@ -139,6 +141,7 @@ Yii Framework 2 Change Log
- Chg #2173: Removed `StringHelper::diff()`, Moved `phpspec/php-diff` dependency from `yiisoft/yii2` to `yiisoft/yii2-gii` (samdark)
- Chg #2175: QueryBuilder will now append UNION statements at the end of the primary SQL (qiangxue)
- Chg #2210: Mysql driver will now treat `tinyint(1)` as integer instead of boolean (qiangxue)
- Chg #2248: Renamed `yii\base\Model::DEFAULT_SCENARIO` to `yii\base\Model::SCENARIO_DEFAULT` (samdark)
- Chg: Renamed `yii\jui\Widget::clientEventsMap` to `clientEventMap` (qiangxue)
- Chg: Renamed `ActiveRecord::getPopulatedRelations()` to `getRelatedRecords()` (qiangxue)
- Chg: Renamed `attributeName` and `className` to `targetAttribute` and `targetClass` for `UniqueValidator` and `ExistValidator` (qiangxue)
......
......@@ -515,7 +515,7 @@ abstract class Application extends Module
$handler->handle($exception);
} else {
echo $this->renderException($exception);
if (PHP_SAPI === 'cli') {
if (PHP_SAPI === 'cli' && !YII_ENV_TEST) {
exit(1);
}
}
......
......@@ -87,7 +87,10 @@ class ErrorHandler extends Component
{
if (Yii::$app instanceof \yii\console\Application || YII_ENV_TEST) {
echo Yii::$app->renderException($exception);
exit(1);
if (!YII_ENV_TEST) {
exit(1);
}
return;
}
$useErrorView = !YII_DEBUG || $exception instanceof UserException;
......
......@@ -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_SCENARIO]].
* @property string $scenario The scenario that this model is in. Defaults to [[SCENARIO_DEFAULT]].
* @property ArrayObject|\yii\validators\Validator[] $validators All the validators declared in the model.
* This property is read-only.
*
......@@ -57,7 +57,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
/**
* The name of the default scenario.
*/
const DEFAULT_SCENARIO = 'default';
const SCENARIO_DEFAULT = 'default';
/**
* @event ModelEvent an event raised at the beginning of [[validate()]]. You may set
......@@ -80,7 +80,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
/**
* @var string current scenario
*/
private $_scenario = self::DEFAULT_SCENARIO;
private $_scenario = self::SCENARIO_DEFAULT;
/**
* Returns the validation rules for attributes.
......@@ -170,7 +170,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
* please prefix the attribute with an exclamation character (e.g. '!rank').
*
* 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
* declaration. A special scenario named [[SCENARIO_DEFAULT]] 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.
*
......@@ -178,7 +178,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
*/
public function scenarios()
{
$scenarios = [self::DEFAULT_SCENARIO => []];
$scenarios = [self::SCENARIO_DEFAULT => []];
foreach ($this->getValidators() as $validator) {
foreach ($validator->on as $scenario) {
$scenarios[$scenario] = [];
......@@ -214,7 +214,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
}
foreach ($scenarios as $scenario => $attributes) {
if (empty($attributes) && $scenario !== self::DEFAULT_SCENARIO) {
if (empty($attributes) && $scenario !== self::SCENARIO_DEFAULT) {
unset($scenarios[$scenario]);
} else {
$scenarios[$scenario] = array_keys($attributes);
......@@ -649,7 +649,7 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
* 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_SCENARIO]].
* @return string the scenario that this model is in. Defaults to [[SCENARIO_DEFAULT]].
*/
public function getScenario()
{
......
......@@ -275,6 +275,28 @@ class ActiveRecord extends BaseActiveRecord
}
/**
* @inheritdoc
*/
public static function create($row)
{
$record = static::instantiate($row);
$attributes = array_flip($record->attributes());
$columns = static::getTableSchema()->columns;
foreach ($row as $name => $value) {
if (isset($columns[$name])) {
$value = $columns[$name]->typecast($value);
}
if (isset($attributes[$name])) {
$record->setAttribute($name, $value);
} else {
$record->$name = $value;
}
}
$record->setOldAttributes($record->getAttributes());
return $record;
}
/**
* Inserts a row into the associated database table using the attribute values of this record.
*
* This method performs the following steps in order:
......
......@@ -409,15 +409,12 @@ abstract class Schema extends Object
static $typeMap = [ // abstract type => php type
'smallint' => 'integer',
'integer' => 'integer',
'bigint' => 'integer',
'boolean' => 'boolean',
'float' => 'double',
];
if (isset($typeMap[$column->type])) {
if ($column->type === 'bigint') {
return PHP_INT_SIZE == 8 && !$column->unsigned ? 'integer' : 'string';
} elseif ($column->type === 'integer') {
return PHP_INT_SIZE == 4 && $column->unsigned ? 'string' : 'integer';
if ($column->type === 'integer') {
return $column->unsigned ? 'string' : 'integer';
} else {
return $typeMap[$column->type];
}
......
......@@ -111,9 +111,9 @@ class QueryBuilder extends \yii\db\QueryBuilder
$value = (int)$value - 1;
}
try {
// it's possible sqlite_sequence does not exist
$db->createCommand("UPDATE sqlite_sequence SET seq='$value' WHERE name='{$table->name}'")->execute();
} catch (Exception $e) {
// it's possible that sqlite_sequence does not exist
}
} elseif ($table === null) {
throw new InvalidParamException("Table not found: $tableName");
......
......@@ -14,6 +14,18 @@ use yii\helpers\Html;
/**
* ActionColumn is a column for the [[GridView]] widget that displays buttons for viewing and manipulating the items.
*
* To add an ActionColumn to the gridview, add it to the [[GridView::columns|columns]] configuration as follows:
*
* ```php
* 'columns' => [
* // ...
* [
* 'class' => 'yii\grid\ActionColumn',
* // you may configure additional properties here
* ],
* ]
* ```
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
......
......@@ -13,6 +13,19 @@ use yii\helpers\Html;
/**
* CheckboxColumn displays a column of checkboxes in a grid view.
*
* * To add a CheckboxColumn to the [[GridView]], add it to the [[GridView::columns|columns]] configuration as follows:
*
* ```php
* 'columns' => [
* // ...
* [
* 'class' => 'yii\grid\CheckboxColumn',
* // you may configure additional properties here
* ],
* ]
* ```
*
* Users may click on the checkboxes to select rows of the grid. The selected rows may be
* obtained by calling the following JavaScript code:
*
......
......@@ -67,9 +67,9 @@ class GridView extends BaseListView
* returns an array of the HTML attributes. The anonymous function will be called once for every
* data model returned by [[dataProvider]]. It should have the following signature:
*
* ~~~php
* ```php
* function ($model, $key, $index, $grid)
* ~~~
* ```
*
* - `$model`: the current data model being rendered
* - `$key`: the key value associated with the current data model
......@@ -111,7 +111,7 @@ class GridView extends BaseListView
* @var array grid column configuration. Each array element represents the configuration
* for one particular grid column. For example,
*
* ~~~php
* ```php
* [
* ['class' => SerialColumn::className()],
* [
......@@ -122,7 +122,7 @@ class GridView extends BaseListView
* ],
* ['class' => CheckboxColumn::className()],
* ]
* ~~~
* ```
*
* If a column is of class [[DataColumn]], the "class" element can be omitted.
*
......
......@@ -10,6 +10,18 @@ namespace yii\grid;
/**
* SerialColumn displays a column of row numbers (1-based).
*
* To add a SerialColumn to the [[GridView]], add it to the [[GridView::columns|columns]] configuration as follows:
*
* ```php
* 'columns' => [
* // ...
* [
* 'class' => 'yii\grid\SerialColumn',
* // you may configure additional properties here
* ],
* ]
* ```
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
......
......@@ -422,7 +422,7 @@ class BaseConsole
$styleA = ArrayHelper::merge($styleA, $style);
}
$styleString[] = [];
$styleString = [];
foreach ($styleA as $name => $content) {
if ($name === 'text-decoration') {
$content = implode(' ', $content);
......
......@@ -137,7 +137,7 @@ class MessageFormatter extends Component
}
// replace named arguments
if (($tokens = $this->tokenizePattern($pattern)) === false) {
if (($tokens = self::tokenizePattern($pattern)) === false) {
$this->_errorCode = -1;
$this->_errorMessage = "Message pattern is invalid.";
return false;
......@@ -187,7 +187,7 @@ class MessageFormatter extends Component
*/
private function replaceNamedArguments($pattern, $givenParams, &$resultingParams, &$map = [])
{
if (($tokens = $this->tokenizePattern($pattern)) === false) {
if (($tokens = self::tokenizePattern($pattern)) === false) {
return false;
}
foreach($tokens as $i => $token) {
......@@ -214,7 +214,7 @@ class MessageFormatter extends Component
if (!isset($token[2])) {
return false;
}
$subtokens = $this->tokenizePattern($token[2]);
$subtokens = self::tokenizePattern($token[2]);
$c = count($subtokens);
for ($k = 0; $k + 1 < $c; $k++) {
if (is_array($subtokens[$k]) || !is_array($subtokens[++$k])) {
......@@ -239,7 +239,7 @@ class MessageFormatter extends Component
*/
protected function fallbackFormat($pattern, $args, $locale)
{
if (($tokens = $this->tokenizePattern($pattern)) === false) {
if (($tokens = self::tokenizePattern($pattern)) === false) {
$this->_errorCode = -1;
$this->_errorMessage = "Message pattern is invalid.";
return false;
......@@ -261,7 +261,7 @@ class MessageFormatter extends Component
* @param string $pattern patter to tokenize
* @return array|bool array of tokens or false on failure
*/
private function tokenizePattern($pattern)
private static function tokenizePattern($pattern)
{
$depth = 1;
if (($start = $pos = mb_strpos($pattern, '{')) === false) {
......@@ -340,7 +340,7 @@ class MessageFormatter extends Component
if (!isset($token[2])) {
return false;
}
$select = static::tokenizePattern($token[2]);
$select = self::tokenizePattern($token[2]);
$c = count($select);
$message = false;
for ($i = 0; $i + 1 < $c; $i++) {
......@@ -368,7 +368,7 @@ class MessageFormatter extends Component
if (!isset($token[2])) {
return false;
}
$plural = static::tokenizePattern($token[2]);
$plural = self::tokenizePattern($token[2]);
$c = count($plural);
$message = false;
$offset = 0;
......
......@@ -32,30 +32,30 @@ return array (
'No' => 'Жоқ',
'No help for unknown command "{command}".' => 'Анықтама белгісіз команда үшін ақиық "{command}".',
'No help for unknown sub-command "{command}".' => 'Анықтама белгісіз субкоманда үшін ақиық "{command}".',
'No results found.' => 'Ештене табылган жок.',
'Only files with these extensions are allowed: {extensions}.' => 'Файлды жуктеу тек қана осы аумақтармен: {extensions}.',
'Only files with these mimeTypes are allowed: {mimeTypes}.' => 'Файлды жуктеу тек қана осы MIME-үлгілермен: {mimeTypes}.',
'No results found.' => 'Ештене табылған жок.',
'Only files with these extensions are allowed: {extensions}.' => 'Файлды жүктеу тек қана осы аумақтармен: {extensions}.',
'Only files with these mimeTypes are allowed: {mimeTypes}.' => 'Файлды жүктеу тек қана осы MIME-үлгілермен: {mimeTypes}.',
'Page not found.' => 'Парақ табылган жок.',
'Please fix the following errors:' => 'Мына қателерді түзеніз:',
'Please upload a file.' => 'Файлды жуктеу.',
'Please upload a file.' => 'Файлды жүктеу.',
'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.' => 'Жазбалар көрсетілген <b>{begin, number}-{end, number}</b> дан <b>{totalCount, number}</b>.',
'The file "{file}" is not an image.' => 'Файл «{file}» сурет емес.',
'The file "{file}" is too big. Its size cannot exceed {limit, number} {limit, plural, one{byte} other{bytes}}.' => 'Файлдын «{file}» көлемі өте үлкен. Өлшемі осыдан аспау керек {limit, number} {limit, plural, one{байт} few{байта} many{байт} other{байта}}.',
'The file "{file}" is too small. Its size cannot be smaller than {limit, number} {limit, plural, one{byte} other{bytes}}.' => 'Файлдын «{file}» көлемі өте кіші. Өлшемі осыдан астам болу керек {limit, number} {limit, plural, one{байт} few{байта} many{байт} other{байта}}.',
'The format of {attribute} is invalid.' => 'Форматын мағынасы дұрыс емес «{attribute}».',
'The image "{file}" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Файл «{file}» өте үлкен. Ұзындығы осыдан аспау керек {limit, number} {limit, plural, one{пиксель} few{пикселя} many{пикселей} other{пикселя}}.',
'The image "{file}" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Файл «{file}» өте үлкен. Ені осыдан аспау керек {limit, number} {limit, plural, one{пиксель} few{пикселя} many{пикселей} other{пикселя}}.',
'The image "{file}" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Файл «{file}» өте кіші. Ұзындығы осыдан астам болу керек {limit, number} {limit, plural, one{пиксель} few{пикселя} many{пикселей} other{пикселя}}.',
'The image "{file}" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Файл «{file}» өте кіші. Ені осыдан астам болу керек {limit, number} {limit, plural, one{пиксель} few{пикселя} many{пикселей} other{пикселя}}.',
'The file "{file}" is too big. Its size cannot exceed {limit, number} {limit, plural, one{byte} other{bytes}}.' => 'Файл «{file}» көлемі өте үлкен. Өлшемі осыдан аспау керек,неғұрлым {limit, number} {limit, plural, one{байт} few{байтар} many{байтар} other{байтар}}.',
'The file "{file}" is too small. Its size cannot be smaller than {limit, number} {limit, plural, one{byte} other{bytes}}.' => 'Файл «{file}» көлемі өте кіші. Өлшемі осыдан астам болу керек,неғұрлым {limit, number} {limit, plural, one{байт} few{байтар} many{байтар} other{байтар}}.',
'The format of {attribute} is invalid.' => 'Форматың мағынасы дұрыс емес «{attribute}».',
'The image "{file}" is too large. The height cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Файл «{file}» өте үлкен. Ұзындығы осыдан аспау керек,неғұрлым {limit, number} {limit, plural, one{пиксель} few{пиксельдер} many{пиксельдер} other{пиксельдер}}.',
'The image "{file}" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Файл «{file}» өте үлкен. Ені осыдан аспау керек,неғұрлым {limit, number} {limit, plural, one{пиксель} few{пиксельдер} many{пиксельдер} other{пиксельдер}}.',
'The image "{file}" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Файл «{file}» өте кіші. Ұзындығы осыдан астам болу керек,неғұрлым limit, number} {limit, plural, one{пиксель} few{пиксельдер} many{пиксельдер} other{пиксельдер}}.',
'The image "{file}" is too small. The width cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.' => 'Файл «{file}» өте кіші. Ені осыдан астам болу керек,неғұрлым {limit, number} {limit, plural, one{пиксель} few{пиксельдер} many{пиксельдер} other{пиксельдер}}.',
'The verification code is incorrect.' => 'Тексеріс коды қате.',
'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Барі <b>{count, number}</b> {count, plural, one{жазба} few{жазбалар} many{жазбалардың} other{жазбалар}}.',
'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.' => 'Барі <b>{count, number}</b> {count, plural, one{жазба} few{жазбалар} many{жазбалар} other{жазбалар}}.',
'Unable to verify your data submission.' => 'Берілген мәліметердің тексеру сәті болмады.',
'Unknown command "{command}".' => 'Белгісіз команда "{command}".',
'Unknown option: --{name}' => 'Белгісіз опция: --{name}',
'Update' => 'Редакциялау',
'Yes' => 'Я',
'You are not allowed to perform this action.' => 'Сізге адал әрекет жасауға болмайды',
'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Сіз осыдан жүктеуге астам {limit, number} {limit, plural, one{файла} few{файлов} many{файлов} other{файла}}.',
'You can upload at most {limit, number} {limit, plural, one{file} other{files}}.' => 'Сіз осыдан жүктеуге астам {limit, number} {limit, plural, one{файла} few{файлдар} many{файлдар} other{файлдар}}.',
'the input value' => 'кіргізілген мағыналар',
'{attribute} "{value}" has already been taken.' => '{attribute} «{value}» Бұл бос емес.',
'{attribute} cannot be blank.' => 'Толтыруға қажет «{attribute}».',
......@@ -75,7 +75,7 @@ return array (
'{attribute} must be no less than {min}.' => 'Мағына «{attribute}» көп болу керек {min}.',
'{attribute} must be repeated exactly.' => 'Мағына «{attribute}» дәлме-дәл қайталану керек.',
'{attribute} must not be equal to "{compareValue}".' => 'Мағына «{attribute}» тең болмау керек «{compareValue}».',
'{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => 'Мағына «{attribute}» минимум болу керек {min, number} {min, plural, one{рәміз} few{рәміздер} many{рәміздерді} other{рәміздер}}.',
'{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => 'Мағына «{attribute}» өте үлкен болау керек {max, number} {max, plural, one{рәміз} few{рәміздер} many{рәміздерді} other{рәміздер}}.',
'{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => 'Мағына «{attribute}» болу керек {length, number} {length, plural, one{рәміз} few{рәміздер} many{рәміздерді} other{рәміздер}}.',
'{attribute} should contain at least {min, number} {min, plural, one{character} other{characters}}.' => 'Мағына «{attribute}» минимум болу керек {min, number} {min, plural, one{рәміз} few{рәміздер} many{рәміздер} other{рәміздер}}.',
'{attribute} should contain at most {max, number} {max, plural, one{character} other{characters}}.' => 'Мағына «{attribute}» өте үлкен болу керек {max, number} {max, plural, one{рәміз} few{рәміздер} many{рәміздер} other{рәміздер}}.',
'{attribute} should contain {length, number} {length, plural, one{character} other{characters}}.' => 'Мағынада «{attribute}» болу керек {length, number} {length, plural, one{рәміз} few{рәміздер} many{рәміздер} other{рәміздер}}.',
);
......@@ -225,6 +225,14 @@ class FileValidator extends Validator
}
/**
* @inheritdoc
*/
public function isEmpty($value, $trim = false)
{
return !$value instanceof UploadedFile || $value->error == UPLOAD_ERR_NO_FILE;
}
/**
* Converts php.ini style size to bytes
*
* @param string $sizeStr $sizeStr
......
......@@ -616,6 +616,8 @@ class ActiveField extends Component
return [];
}
$options = [];
$enableClientValidation = $this->enableClientValidation || $this->enableClientValidation === null && $this->form->enableClientValidation;
if ($enableClientValidation) {
$validators = [];
......
......@@ -172,7 +172,17 @@ class FileValidatorTest extends TestCase
$val->validateAttribute($m, 'attr_files_empty');
$this->assertTrue($m->hasErrors('attr_files_empty'));
$this->assertSame($val->uploadRequired, current($m->getErrors('attr_files_empty')));
// single File with skipOnEmpty=false
$val = new FileValidator(['skipOnEmpty' => false]);
$m = $this->createModelForAttributeTest();
$val->validateAttribute($m, 'attr_files');
$this->assertFalse($m->hasErrors());
$val->validateAttribute($m, 'attr_files_empty');
$this->assertTrue($m->hasErrors('attr_files_empty'));
$this->assertSame($val->uploadRequired, current($m->getErrors('attr_files_empty')));
$m = $this->createModelForAttributeTest();
// too big
$val = new FileValidator(['maxSize' => 128]);
$val->validateAttribute($m, 'attr_files');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment