Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yii2
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
PSDI Army
yii2
Commits
d5bd6816
Commit
d5bd6816
authored
Aug 03, 2014
by
RichWeber
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of github.com:yiisoft/yii2
parents
9fa9dee3
f0fefbb6
Hide whitespace changes
Inline
Side-by-side
Showing
32 changed files
with
646 additions
and
258 deletions
+646
-258
main.php
apps/advanced/backend/config/main.php
+0
-4
composer.json
apps/advanced/composer.json
+1
-6
main-local.php
apps/advanced/environments/dev/backend/config/main-local.php
+8
-1
main-local.php
.../advanced/environments/dev/frontend/config/main-local.php
+8
-1
index.php
apps/advanced/environments/index.php
+19
-5
main-local.php
.../advanced/environments/prod/backend/config/main-local.php
+6
-0
main-local.php
...advanced/environments/prod/frontend/config/main-local.php
+6
-0
main.php
apps/advanced/frontend/config/main.php
+0
-4
init
apps/advanced/init
+138
-112
db-migrations.md
docs/guide/db-migrations.md
+1
-1
db-query-builder.md
docs/guide/db-query-builder.md
+13
-2
tutorial-template-engines.md
docs/guide/tutorial-template-engines.md
+9
-3
Generator.php
extensions/gii/generators/model/Generator.php
+10
-10
QueryBuilder.php
extensions/sphinx/QueryBuilder.php
+28
-4
ViewRenderer.php
extensions/twig/ViewRenderer.php
+18
-4
CHANGELOG.md
framework/CHANGELOG.md
+2
-0
yii.validation.js
framework/assets/yii.validation.js
+110
-43
Model.php
framework/base/Model.php
+6
-5
AttributeBehavior.php
framework/behaviors/AttributeBehavior.php
+4
-1
BlameableBehavior.php
framework/behaviors/BlameableBehavior.php
+2
-0
TimestampBehavior.php
framework/behaviors/TimestampBehavior.php
+3
-1
MessageController.php
framework/console/controllers/MessageController.php
+3
-3
QueryBuilder.php
framework/db/QueryBuilder.php
+28
-4
QueryTrait.php
framework/db/QueryTrait.php
+3
-15
FileValidator.php
framework/validators/FileValidator.php
+41
-18
ImageValidator.php
framework/validators/ImageValidator.php
+61
-1
Singer.php
tests/unit/data/base/Singer.php
+2
-0
GeneratorsTest.php
tests/unit/extensions/gii/GeneratorsTest.php
+6
-1
ModelTest.php
tests/unit/framework/base/ModelTest.php
+8
-1
QueryBuilderTest.php
tests/unit/framework/db/QueryBuilderTest.php
+98
-4
PhpManagerTest.php
tests/unit/framework/rbac/PhpManagerTest.php
+1
-1
ActiveFieldTest.php
tests/unit/framework/widgets/ActiveFieldTest.php
+3
-3
No files found.
apps/advanced/backend/config/main.php
View file @
d5bd6816
...
...
@@ -13,10 +13,6 @@ return [
'bootstrap'
=>
[
'log'
],
'modules'
=>
[],
'components'
=>
[
'request'
=>
[
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
'cookieValidationKey'
=>
''
,
],
'user'
=>
[
'identityClass'
=>
'common\models\User'
,
'enableAutoLogin'
=>
true
,
...
...
apps/advanced/composer.json
View file @
d5bd6816
...
...
@@ -32,8 +32,7 @@
},
"scripts"
:
{
"post-create-project-cmd"
:
[
"yii
\\
composer
\\
Installer::setPermission"
,
"yii
\\
composer
\\
Installer::generateCookieValidationKey"
"yii
\\
composer
\\
Installer::setPermission"
]
},
"config"
:
{
...
...
@@ -46,10 +45,6 @@
"frontend/runtime"
,
"frontend/web/assets"
],
"config"
:
[
"frontend/config/main.php"
,
"backend/config/main.php"
]
}
}
apps/advanced/environments/dev/backend/config/main-local.php
View file @
d5bd6816
<?php
$config
=
[];
$config
=
[
'components'
=>
[
'request'
=>
[
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
'cookieValidationKey'
=>
''
,
],
],
];
if
(
!
YII_ENV_TEST
)
{
// configuration adjustments for 'dev' environment
...
...
apps/advanced/environments/dev/frontend/config/main-local.php
View file @
d5bd6816
<?php
$config
=
[];
$config
=
[
'components'
=>
[
'request'
=>
[
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
'cookieValidationKey'
=>
''
,
],
],
];
if
(
!
YII_ENV_TEST
)
{
// configuration adjustments for 'dev' environment
...
...
apps/advanced/environments/index.php
View file @
d5bd6816
...
...
@@ -9,9 +9,15 @@
* return [
* 'environment name' => [
* 'path' => 'directory storing the local files',
* '
w
ritable' => [
* '
setW
ritable' => [
* // list of directories that should be set writable
* ],
* 'setExecutable' => [
* // list of directories that should be set executable
* ],
* 'setCookieValidationKey' => [
* // list of config files that need to be inserted with automatically generated cookie validation keys
* ],
* ],
* ];
* ```
...
...
@@ -19,26 +25,34 @@
return
[
'Development'
=>
[
'path'
=>
'dev'
,
'
w
ritable'
=>
[
'
setW
ritable'
=>
[
'backend/runtime'
,
'backend/web/assets'
,
'frontend/runtime'
,
'frontend/web/assets'
,
],
'
e
xecutable'
=>
[
'
setE
xecutable'
=>
[
'yii'
,
],
'setCookieValidationKey'
=>
[
'backend/config/main-local.php'
,
'frontend/config/main-local.php'
,
],
],
'Production'
=>
[
'path'
=>
'prod'
,
'
w
ritable'
=>
[
'
setW
ritable'
=>
[
'backend/runtime'
,
'backend/web/assets'
,
'frontend/runtime'
,
'frontend/web/assets'
,
],
'
e
xecutable'
=>
[
'
setE
xecutable'
=>
[
'yii'
,
],
'setCookieValidationKey'
=>
[
'backend/config/main-local.php'
,
'frontend/config/main-local.php'
,
],
],
];
apps/advanced/environments/prod/backend/config/main-local.php
View file @
d5bd6816
<?php
return
[
'components'
=>
[
'request'
=>
[
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
'cookieValidationKey'
=>
''
,
],
],
];
apps/advanced/environments/prod/frontend/config/main-local.php
View file @
d5bd6816
<?php
return
[
'components'
=>
[
'request'
=>
[
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
'cookieValidationKey'
=>
''
,
],
],
];
apps/advanced/frontend/config/main.php
View file @
d5bd6816
...
...
@@ -12,10 +12,6 @@ return [
'bootstrap'
=>
[
'log'
],
'controllerNamespace'
=>
'frontend\controllers'
,
'components'
=>
[
'request'
=>
[
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
'cookieValidationKey'
=>
''
,
],
'user'
=>
[
'identityClass'
=>
'common\models\User'
,
'enableAutoLogin'
=>
true
,
...
...
apps/advanced/init
View file @
d5bd6816
...
...
@@ -14,6 +14,10 @@
* @license http://www.yiiframework.com/license/
*/
if
(
!
extension_loaded
(
'mcrypt'
))
{
die
(
'The mcrypt PHP extension is required by Yii2.'
);
}
$params
=
getParams
();
$root
=
str_replace
(
'\\'
,
'/'
,
__DIR__
);
$envs
=
require
(
"
$root
/environments/index.php"
);
...
...
@@ -23,147 +27,169 @@ echo "Yii Application Initialization Tool v1.0\n\n";
$envName
=
null
;
if
(
empty
(
$params
[
'env'
])
||
$params
[
'env'
]
===
'1'
)
{
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
(
0
);
}
if
(
isset
(
$envNames
[
$answer
]))
{
$envName
=
$envNames
[
$answer
];
}
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
(
0
);
}
if
(
isset
(
$envNames
[
$answer
]))
{
$envName
=
$envNames
[
$answer
];
}
}
else
{
$envName
=
$params
[
'env'
];
$envName
=
$params
[
'env'
];
}
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
);
$envsList
=
implode
(
', '
,
$envNames
);
echo
"
\n
$envName
is not a valid environment. Try one of the following:
$envsList
.
\n
"
;
exit
(
2
);
}
$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
(
0
);
}
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
(
0
);
}
}
echo
"
\n
Start initialization ...
\n\n
"
;
$files
=
getFileList
(
"
$root
/environments/
{
$env
[
'path'
]
}
"
);
$all
=
false
;
foreach
(
$files
as
$file
)
{
if
(
!
copyFile
(
$root
,
"environments/
{
$env
[
'path'
]
}
/
$file
"
,
$file
,
$all
,
$params
))
{
break
;
}
}
if
(
isset
(
$env
[
'writable'
]))
{
foreach
(
$env
[
'writable'
]
as
$writable
)
{
echo
" chmod 0777
$writable
\n
"
;
@
chmod
(
"
$root
/
$writable
"
,
0777
);
}
if
(
!
copyFile
(
$root
,
"environments/
{
$env
[
'path'
]
}
/
$file
"
,
$file
,
$all
,
$params
))
{
break
;
}
}
if
(
isset
(
$env
[
'executable'
]))
{
foreach
(
$env
[
'executable'
]
as
$executable
)
{
echo
" chmod 0755
$executable
\n
"
;
@
chmod
(
"
$root
/
$executable
"
,
0755
);
}
$callbacks
=
[
'setCookieValidationKey'
,
'setWritable'
,
'setExecutable'
];
foreach
(
$callbacks
as
$callback
)
{
if
(
!
empty
(
$env
[
$callback
]))
{
$callback
(
$root
,
$env
[
$callback
]
);
}
}
echo
"
\n
... initialization completed.
\n\n
"
;
function
getFileList
(
$root
,
$basePath
=
''
)
{
$files
=
[];
$handle
=
opendir
(
$root
);
while
((
$path
=
readdir
(
$handle
))
!==
false
)
{
if
(
$path
===
'.svn'
||
$path
===
'.'
||
$path
===
'..'
)
{
continue
;
}
$fullPath
=
"
$root
/
$path
"
;
$relativePath
=
$basePath
===
''
?
$path
:
"
$basePath
/
$path
"
;
if
(
is_dir
(
$fullPath
))
{
$files
=
array_merge
(
$files
,
getFileList
(
$fullPath
,
$relativePath
));
}
else
{
$files
[]
=
$relativePath
;
}
}
closedir
(
$handle
);
return
$files
;
$files
=
[];
$handle
=
opendir
(
$root
);
while
((
$path
=
readdir
(
$handle
))
!==
false
)
{
if
(
$path
===
'.svn'
||
$path
===
'.'
||
$path
===
'..'
)
{
continue
;
}
$fullPath
=
"
$root
/
$path
"
;
$relativePath
=
$basePath
===
''
?
$path
:
"
$basePath
/
$path
"
;
if
(
is_dir
(
$fullPath
))
{
$files
=
array_merge
(
$files
,
getFileList
(
$fullPath
,
$relativePath
));
}
else
{
$files
[]
=
$relativePath
;
}
}
closedir
(
$handle
);
return
$files
;
}
function
copyFile
(
$root
,
$source
,
$target
,
&
$all
,
$params
)
{
if
(
!
is_file
(
$root
.
'/'
.
$source
))
{
echo
" skip
$target
(
$source
not exist)
\n
"
;
return
true
;
}
if
(
is_file
(
$root
.
'/'
.
$target
))
{
if
(
file_get_contents
(
$root
.
'/'
.
$source
)
===
file_get_contents
(
$root
.
'/'
.
$target
))
{
echo
" unchanged
$target
\n
"
;
return
true
;
}
if
(
$all
)
{
echo
" overwrite
$target
\n
"
;
}
else
{
echo
" exist
$target
\n
"
;
echo
" ...overwrite? [Yes|No|All|Quit] "
;
$answer
=
!
empty
(
$params
[
'overwrite'
])
?
$params
[
'overwrite'
]
:
trim
(
fgets
(
STDIN
));
if
(
!
strncasecmp
(
$answer
,
'q'
,
1
))
{
return
false
;
}
else
{
if
(
!
strncasecmp
(
$answer
,
'y'
,
1
))
{
echo
" overwrite
$target
\n
"
;
}
else
{
if
(
!
strncasecmp
(
$answer
,
'a'
,
1
))
{
echo
" overwrite
$target
\n
"
;
$all
=
true
;
}
else
{
echo
" skip
$target
\n
"
;
return
true
;
}
}
}
}
file_put_contents
(
$root
.
'/'
.
$target
,
file_get_contents
(
$root
.
'/'
.
$source
));
return
true
;
}
echo
" generate
$target
\n
"
;
@
mkdir
(
dirname
(
$root
.
'/'
.
$target
),
0777
,
true
);
file_put_contents
(
$root
.
'/'
.
$target
,
file_get_contents
(
$root
.
'/'
.
$source
));
return
true
;
if
(
!
is_file
(
$root
.
'/'
.
$source
))
{
echo
" skip
$target
(
$source
not exist)
\n
"
;
return
true
;
}
if
(
is_file
(
$root
.
'/'
.
$target
))
{
if
(
file_get_contents
(
$root
.
'/'
.
$source
)
===
file_get_contents
(
$root
.
'/'
.
$target
))
{
echo
" unchanged
$target
\n
"
;
return
true
;
}
if
(
$all
)
{
echo
" overwrite
$target
\n
"
;
}
else
{
echo
" exist
$target
\n
"
;
echo
" ...overwrite? [Yes|No|All|Quit] "
;
$answer
=
!
empty
(
$params
[
'overwrite'
])
?
$params
[
'overwrite'
]
:
trim
(
fgets
(
STDIN
));
if
(
!
strncasecmp
(
$answer
,
'q'
,
1
))
{
return
false
;
}
else
{
if
(
!
strncasecmp
(
$answer
,
'y'
,
1
))
{
echo
" overwrite
$target
\n
"
;
}
else
{
if
(
!
strncasecmp
(
$answer
,
'a'
,
1
))
{
echo
" overwrite
$target
\n
"
;
$all
=
true
;
}
else
{
echo
" skip
$target
\n
"
;
return
true
;
}
}
}
}
file_put_contents
(
$root
.
'/'
.
$target
,
file_get_contents
(
$root
.
'/'
.
$source
));
return
true
;
}
echo
" generate
$target
\n
"
;
@
mkdir
(
dirname
(
$root
.
'/'
.
$target
),
0777
,
true
);
file_put_contents
(
$root
.
'/'
.
$target
,
file_get_contents
(
$root
.
'/'
.
$source
));
return
true
;
}
function
getParams
()
{
$rawParams
=
[];
if
(
isset
(
$_SERVER
[
'argv'
]))
{
$rawParams
=
$_SERVER
[
'argv'
];
array_shift
(
$rawParams
);
}
$params
=
[];
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
;
$rawParams
=
[];
if
(
isset
(
$_SERVER
[
'argv'
]))
{
$rawParams
=
$_SERVER
[
'argv'
];
array_shift
(
$rawParams
);
}
$params
=
[];
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
;
}
function
setWritable
(
$root
,
$paths
)
{
foreach
(
$paths
as
$writable
)
{
echo
" chmod 0777
$writable
\n
"
;
@
chmod
(
"
$root
/
$writable
"
,
0777
);
}
}
function
setExecutable
(
$root
,
$paths
)
{
foreach
(
$paths
as
$executable
)
{
echo
" chmod 0755
$executable
\n
"
;
@
chmod
(
"
$root
/
$executable
"
,
0755
);
}
}
function
setCookieValidationKey
(
$root
,
$paths
)
{
foreach
(
$paths
as
$file
)
{
echo
" generate cookie validation key in
$file
\n
"
;
$file
=
$root
.
'/'
.
$file
;
$length
=
32
;
$bytes
=
mcrypt_create_iv
(
$length
,
MCRYPT_DEV_URANDOM
);
$key
=
strtr
(
substr
(
base64_encode
(
$bytes
),
0
,
$length
),
'+/='
,
'_-.'
);
$content
=
preg_replace
(
'/(("|\')cookieValidationKey("|\')\s*=>\s*)(""|\'\')/'
,
"
\\
1'
$key
'"
,
file_get_contents
(
$file
));
file_put_contents
(
$file
,
$content
);
}
}
docs/guide/db-migrations.md
View file @
d5bd6816
...
...
@@ -97,7 +97,7 @@ class m101129_185401_create_news_table extends \yii\db\Migration
}
```
The base class
[
\yii\db\Migration
]
exposes a database connection via
`db`
The base class
[
[\yii\db\Migration
]
] exposes a database connection via
`db`
property. You can use it for manipulating data and schema of a database.
The column types used in this example are abstract types that will be replaced
...
...
docs/guide/db-query-builder.md
View file @
d5bd6816
...
...
@@ -249,6 +249,19 @@ Operator can be one of the following:
-
`not exists`
: similar to the
`exists`
operator and builds a
`NOT EXISTS (sub-query)`
expression.
Additionally you can specify anything as operator:
```
php
$userQuery
=
(
new
Query
)
->
select
(
'id'
)
->
from
(
'user'
);
$query
->
where
([
'>='
,
'id'
,
10
]);
```
It will result in:
```
sql
SELECT
id
FROM
user
WHERE
id
>=
10
;
```
If you are building parts of condition dynamically it's very convenient to use
`andWhere()`
and
`orWhere()`
:
```
php
...
...
@@ -305,8 +318,6 @@ $query->orderBy([
Here we are ordering by
`id`
ascending and then by
`name`
descending.
```
### `GROUP BY` and `HAVING`
In order to add
`GROUP BY`
to generated SQL you can use the following:
...
...
docs/guide/tutorial-template-engines.md
View file @
d5bd6816
...
...
@@ -56,7 +56,7 @@ return $this->render('renderer.twig', ['username' => 'Alex']);
### Template syntax
The best resource to learn Twig basics is its official documentation you can find at
[
twig.sensiolabs.org
](
http://twig.sensiolabs.org/documentation
)
. Additionally there are Yii-specific
addt
ions
[
twig.sensiolabs.org
](
http://twig.sensiolabs.org/documentation
)
. Additionally there are Yii-specific
syntax extens
ions
described below.
#### Method and function calls
...
...
@@ -271,7 +271,13 @@ or `$this->renderPartial()` controller calls:
return
$this
->
render
(
'renderer.tpl'
,
[
'username'
=>
'Alex'
]);
```
### Additional functions
### Template syntax
The best resource to learn Smarty template syntax is its official documentation you can find at
[
www.smarty.net
](
http://www.smarty.net/docs/en/
)
. Additionally there are Yii-specific syntax extensions
described below.
#### Additional functions
Yii adds the following construct to the standard Smarty syntax:
...
...
@@ -281,7 +287,7 @@ Yii adds the following construct to the standard Smarty syntax:
Internally, the
`path()`
function calls Yii's
`Url::to()`
method.
### Additional variables
###
#
Additional variables
Within Smarty templates, you can also make use of these variables:
...
...
extensions/gii/generators/model/Generator.php
View file @
d5bd6816
...
...
@@ -197,7 +197,7 @@ class Generator extends \yii\gii\Generator
$labels
[
$column
->
name
]
=
'ID'
;
}
else
{
$label
=
Inflector
::
camel2words
(
$column
->
name
);
if
(
!
empty
(
$label
)
&&
substr_compare
(
$label
,
' id'
,
-
3
,
3
,
true
))
{
if
(
!
empty
(
$label
)
&&
substr_compare
(
$label
,
' id'
,
-
3
,
3
,
true
)
===
0
)
{
$label
=
substr
(
$label
,
0
,
-
3
)
.
' ID'
;
}
$labels
[
$column
->
name
]
=
$label
;
...
...
@@ -508,16 +508,16 @@ class Generator extends \yii\gii\Generator
}
}
pr
ivate
$_
tableNames
;
pr
ivate
$_
classNames
;
pr
otected
$
tableNames
;
pr
otected
$
classNames
;
/**
* @return array the table names that match the pattern specified by [[tableName]].
*/
protected
function
getTableNames
()
{
if
(
$this
->
_
tableNames
!==
null
)
{
return
$this
->
_
tableNames
;
if
(
$this
->
tableNames
!==
null
)
{
return
$this
->
tableNames
;
}
$db
=
$this
->
getDbConnection
();
if
(
$db
===
null
)
{
...
...
@@ -540,10 +540,10 @@ class Generator extends \yii\gii\Generator
}
}
elseif
((
$table
=
$db
->
getTableSchema
(
$this
->
tableName
,
true
))
!==
null
)
{
$tableNames
[]
=
$this
->
tableName
;
$this
->
_
classNames
[
$this
->
tableName
]
=
$this
->
modelClass
;
$this
->
classNames
[
$this
->
tableName
]
=
$this
->
modelClass
;
}
return
$this
->
_
tableNames
=
$tableNames
;
return
$this
->
tableNames
=
$tableNames
;
}
/**
...
...
@@ -574,8 +574,8 @@ class Generator extends \yii\gii\Generator
*/
protected
function
generateClassName
(
$tableName
)
{
if
(
isset
(
$this
->
_
classNames
[
$tableName
]))
{
return
$this
->
_
classNames
[
$tableName
];
if
(
isset
(
$this
->
classNames
[
$tableName
]))
{
return
$this
->
classNames
[
$tableName
];
}
if
((
$pos
=
strrpos
(
$tableName
,
'.'
))
!==
false
)
{
...
...
@@ -601,7 +601,7 @@ class Generator extends \yii\gii\Generator
}
}
return
$this
->
_
classNames
[
$tableName
]
=
Inflector
::
id2camel
(
$className
,
'_'
);
return
$this
->
classNames
[
$tableName
]
=
Inflector
::
id2camel
(
$className
,
'_'
);
}
/**
...
...
extensions/sphinx/QueryBuilder.php
View file @
d5bd6816
...
...
@@ -617,12 +617,11 @@ class QueryBuilder extends Object
$operator
=
strtoupper
(
$condition
[
0
]);
if
(
isset
(
$builders
[
$operator
]))
{
$method
=
$builders
[
$operator
];
array_shift
(
$condition
);
return
$this
->
$method
(
$indexes
,
$operator
,
$condition
,
$params
);
}
else
{
throw
new
Exception
(
'Found unknown operator in query: '
.
$operator
)
;
$method
=
'buildSimpleCondition'
;
}
array_shift
(
$condition
);
return
$this
->
$method
(
$indexes
,
$operator
,
$condition
,
$params
);
}
else
{
// hash format: 'column1' => 'value1', 'column2' => 'value2', ...
return
$this
->
buildHashCondition
(
$indexes
,
$condition
,
$params
);
...
...
@@ -986,4 +985,29 @@ class QueryBuilder extends Object
return
$phName
;
}
}
/**
* Creates an SQL expressions like `"column" operator value`.
* @param string $operator the operator to use. Anything could be used e.g. `>`, `<=`, etc.
* @param array $operands contains two column names.
* @param array $params the binding parameters to be populated
* @return string the generated SQL expression
*/
public
function
buildSimpleCondition
(
$operator
,
$operands
,
&
$params
)
{
if
(
count
(
$operands
)
!==
2
)
{
throw
new
InvalidParamException
(
"Operator '
$operator
' requires two operands."
);
}
list
(
$column
,
$value
)
=
$operands
;
if
(
strpos
(
$column
,
'('
)
===
false
)
{
$column
=
$this
->
db
->
quoteColumnName
(
$column
);
}
$phName
=
self
::
PARAM_PREFIX
.
count
(
$params
);
$params
[
$phName
]
=
$value
===
null
?
'NULL'
:
$value
;
return
"
$column
$operator
$phName
"
;
}
}
extensions/twig/ViewRenderer.php
View file @
d5bd6816
...
...
@@ -144,16 +144,30 @@ class ViewRenderer extends BaseViewRenderer
{
$this
->
twig
->
addGlobal
(
'this'
,
$view
);
$loader
=
new
\Twig_Loader_Filesystem
(
dirname
(
$file
));
foreach
(
Yii
::
$aliases
as
$alias
=>
$path
)
{
$loader
->
addPath
(
$path
,
substr
(
$alias
,
1
));
}
$this
->
addAliases
(
$loader
,
Yii
::
$aliases
);
$this
->
twig
->
setLoader
(
$loader
);
return
$this
->
twig
->
render
(
pathinfo
(
$file
,
PATHINFO_BASENAME
),
$params
);
}
/**
* Adds aliases
*
* @param \Twig_Loader_Filesystem $loader
* @param array $aliases
*/
protected
function
addAliases
(
$loader
,
$aliases
)
{
foreach
(
$aliases
as
$alias
=>
$path
)
{
if
(
is_array
(
$path
))
{
$this
->
addAliases
(
$loader
,
$path
);
}
elseif
(
is_string
(
$path
)
&&
is_dir
(
$path
))
{
$loader
->
addPath
(
$path
,
substr
(
$alias
,
1
));
}
}
}
/**
* Adds global objects or static classes
* @param array $globals @see self::$globals
*/
...
...
framework/CHANGELOG.md
View file @
d5bd6816
...
...
@@ -90,6 +90,7 @@ Yii Framework 2 Change Log
-
Enh #1388: Added mapping from physical types to abstract types for OCI DB driver (qiangxue)
-
Enh #1452: Added
`Module::getInstance()`
to allow accessing the module instance from anywhere within the module (qiangxue)
-
Enh #2264:
`CookieCollection::has()`
will return false for expired or removed cookies (qiangxue)
-
Enh #2315: Any operator now could be used with
`yii\db\Query::->where()`
operand format (samdark)
-
Enh #2435:
`yii\db\IntegrityException`
is now thrown on database integrity errors instead of general
`yii\db\Exception`
(samdark)
-
Enh #2558: Enhanced support for memcached by adding
`yii\caching\MemCache::persistentId`
and
`yii\caching\MemCache::options`
(qiangxue)
-
Enh #2837: Error page now shows arguments in stack trace method calls (samdark)
...
...
@@ -167,6 +168,7 @@ Yii Framework 2 Change Log
-
Enh #4436: Added callback functions to AJAX-based form validation (thiagotalma)
-
Enh #4485: Added support for deferred validation in
`ActiveForm`
(Alex-Code)
-
Enh #4520: Added sasl support to
`yii\caching\MemCache`
(xjflyttp)
-
Enh #4566: Added client validation support for image validator (Skysplit, qiangxue)
-
Enh: Added support for using sub-queries when building a DB query with
`IN`
condition (qiangxue)
-
Enh: Supported adding a new response formatter without the need to reconfigure existing formatters (qiangxue)
-
Enh: Added
`yii\web\UrlManager::addRules()`
to simplify adding new URL rules (qiangxue)
...
...
framework/assets/yii.validation.js
View file @
d5bd6816
...
...
@@ -68,55 +68,67 @@ yii.validation = (function ($) {
pub
.
addMessage
(
messages
,
options
.
notEqual
,
value
);
}
},
file
:
function
(
value
,
messages
,
options
,
attribute
)
{
var
files
=
$
(
attribute
.
input
).
get
(
0
).
files
,
index
,
ext
;
if
(
options
.
message
&&
!
files
)
{
pub
.
addMessage
(
messages
,
options
.
message
,
value
);
}
if
(
!
options
.
skipOnEmpty
&&
files
.
length
==
0
)
{
pub
.
addMessage
(
messages
,
options
.
uploadRequired
,
value
);
}
else
if
(
files
.
length
==
0
)
{
return
;
}
if
(
options
.
maxFiles
&&
options
.
maxFiles
<
files
.
length
)
{
pub
.
addMessage
(
messages
,
options
.
tooMany
);
}
file
:
function
(
attribute
,
messages
,
options
)
{
var
files
=
getUploadedFiles
(
attribute
,
messages
,
options
);
$
.
each
(
files
,
function
(
i
,
file
)
{
if
(
options
.
extensions
&&
options
.
extensions
.
length
>
0
)
{
index
=
file
.
name
.
lastIndexOf
(
'.'
);
if
(
!~
index
)
{
ext
=
''
;
}
else
{
ext
=
file
.
name
.
substr
(
index
+
1
,
file
.
name
.
length
).
toLowerCase
();
}
validateFile
(
file
,
messages
,
options
);
});
},
image
:
function
(
attribute
,
messages
,
options
,
deferred
)
{
var
files
=
getUploadedFiles
(
attribute
,
messages
,
options
);
$
.
each
(
files
,
function
(
i
,
file
)
{
validateFile
(
file
,
messages
,
options
);
if
(
!~
options
.
extensions
.
indexOf
(
ext
))
{
messages
.
push
(
options
.
wrongExtension
.
replace
(
/
\{
file
\}
/g
,
file
.
name
));
}
// Skip image validation if FileReader API is not available
if
(
typeof
FileReader
===
"undefined"
)
{
return
;
}
if
(
options
.
mimeTypes
&&
options
.
mimeTypes
.
length
>
0
)
{
if
(
!~
options
.
mimeTypes
.
indexOf
(
file
.
type
))
{
messages
.
push
(
options
.
wrongMimeType
.
replace
(
/
\{
file
\}
/g
,
file
.
name
));
var
def
=
$
.
Deferred
(),
fr
=
new
FileReader
(),
img
=
new
Image
();
img
.
onload
=
function
()
{
if
(
options
.
minWidth
&&
this
.
width
<
options
.
minWidth
)
{
messages
.
push
(
options
.
underWidth
.
replace
(
/
\{
file
\}
/g
,
file
.
name
));
}
}
if
(
options
.
maxSize
&&
options
.
maxSize
<
file
.
size
)
{
messages
.
push
(
options
.
tooBig
.
replace
(
/
\{
file
\}
/g
,
file
.
name
));
}
if
(
options
.
maxSize
&&
options
.
minSize
>
file
.
size
)
{
messages
.
push
(
options
.
tooSmall
.
replace
(
/
\{
file
\}
/g
,
file
.
name
));
}
if
(
options
.
maxWidth
&&
this
.
width
>
options
.
maxWidth
)
{
messages
.
push
(
options
.
overWidth
.
replace
(
/
\{
file
\}
/g
,
file
.
name
));
}
if
(
options
.
minHeight
&&
this
.
height
<
options
.
minHeight
)
{
messages
.
push
(
options
.
underHeight
.
replace
(
/
\{
file
\}
/g
,
file
.
name
));
}
if
(
options
.
maxHeight
&&
this
.
height
>
options
.
maxHeight
)
{
messages
.
push
(
options
.
overHeight
.
replace
(
/
\{
file
\}
/g
,
file
.
name
));
}
def
.
resolve
();
};
img
.
onerror
=
function
()
{
messages
.
push
(
options
.
notImage
);
def
.
resolve
();
};
fr
.
onload
=
function
()
{
img
.
src
=
fr
.
result
;
};
// Resolve deferred if there was error while reading data
fr
.
onerror
=
function
()
{
def
.
resolve
();
};
fr
.
readAsDataURL
(
file
);
deferred
.
push
(
def
);
});
},
number
:
function
(
value
,
messages
,
options
)
{
...
...
@@ -288,5 +300,60 @@ yii.validation = (function ($) {
}
}
};
function
getUploadedFiles
(
attribute
,
messages
,
options
)
{
var
files
=
$
(
attribute
.
input
).
get
(
0
).
files
;
if
(
!
files
)
{
messages
.
push
(
options
.
message
);
return
[];
}
if
(
files
.
length
===
0
)
{
if
(
!
options
.
skipOnEmpty
)
{
messages
.
push
(
options
.
uploadRequired
);
}
return
[];
}
if
(
options
.
maxFiles
&&
options
.
maxFiles
<
files
.
length
)
{
messages
.
push
(
options
.
tooMany
);
return
[];
}
return
files
;
}
function
validateFile
(
file
,
messages
,
options
)
{
if
(
options
.
extensions
&&
options
.
extensions
.
length
>
0
)
{
var
index
,
ext
;
index
=
file
.
name
.
lastIndexOf
(
'.'
);
if
(
!~
index
)
{
ext
=
''
;
}
else
{
ext
=
file
.
name
.
substr
(
index
+
1
,
file
.
name
.
length
).
toLowerCase
();
}
if
(
!~
options
.
extensions
.
indexOf
(
ext
))
{
messages
.
push
(
options
.
wrongExtension
.
replace
(
/
\{
file
\}
/g
,
file
.
name
));
}
}
if
(
options
.
mimeTypes
&&
options
.
mimeTypes
.
length
>
0
)
{
if
(
!~
options
.
mimeTypes
.
indexOf
(
file
.
type
))
{
messages
.
push
(
options
.
wrongMimeType
.
replace
(
/
\{
file
\}
/g
,
file
.
name
));
}
}
if
(
options
.
maxSize
&&
options
.
maxSize
<
file
.
size
)
{
messages
.
push
(
options
.
tooBig
.
replace
(
/
\{
file
\}
/g
,
file
.
name
));
}
if
(
options
.
minSize
&&
options
.
minSize
>
file
.
size
)
{
messages
.
push
(
options
.
tooSmall
.
replace
(
/
\{
file
\}
/g
,
file
.
name
));
}
}
return
pub
;
})(
jQuery
);
framework/base/Model.php
View file @
d5bd6816
...
...
@@ -385,7 +385,6 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
if
(
$this
->
_validators
===
null
)
{
$this
->
_validators
=
$this
->
createValidators
();
}
return
$this
->
_validators
;
}
...
...
@@ -404,7 +403,6 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
$validators
[]
=
$validator
;
}
}
return
$validators
;
}
...
...
@@ -427,7 +425,6 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
throw
new
InvalidConfigException
(
'Invalid validation rule: a rule must specify both attribute names and validator type.'
);
}
}
return
$validators
;
}
...
...
@@ -436,6 +433,12 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
* This is determined by checking if the attribute is associated with a
* [[\yii\validators\RequiredValidator|required]] validation rule in the
* current [[scenario]].
*
* Note that when the validator has a conditional validation applied using
* [[\yii\validators\RequiredValidator::$when|$when]] this method will return
* `false` regardless of the `when` condition because it may be called be
* before the model is loaded with data.
*
* @param string $attribute attribute name
* @return boolean whether the attribute is required
*/
...
...
@@ -446,7 +449,6 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
return
true
;
}
}
return
false
;
}
...
...
@@ -482,7 +484,6 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
public
function
getAttributeLabel
(
$attribute
)
{
$labels
=
$this
->
attributeLabels
();
return
isset
(
$labels
[
$attribute
])
?
$labels
[
$attribute
]
:
$this
->
generateAttributeLabel
(
$attribute
);
}
...
...
framework/behaviors/AttributeBehavior.php
View file @
d5bd6816
...
...
@@ -93,7 +93,10 @@ class AttributeBehavior extends Behavior
$attributes
=
(
array
)
$this
->
attributes
[
$event
->
name
];
$value
=
$this
->
getValue
(
$event
);
foreach
(
$attributes
as
$attribute
)
{
$this
->
owner
->
$attribute
=
$value
;
// ignore attribute names which are not string (e.g. when set by TimestampBehavior::updatedAtAttribute)
if
(
is_string
(
$attribute
))
{
$this
->
owner
->
$attribute
=
$value
;
}
}
}
}
...
...
framework/behaviors/BlameableBehavior.php
View file @
d5bd6816
...
...
@@ -54,10 +54,12 @@ class BlameableBehavior extends AttributeBehavior
{
/**
* @var string the attribute that will receive current user ID value
* Set this property to be null if you do not want to record the creator ID.
*/
public
$createdByAttribute
=
'created_by'
;
/**
* @var string the attribute that will receive current user ID value
* Set this property to be null if you do not want to record the updater ID.
*/
public
$updatedByAttribute
=
'updated_by'
;
/**
...
...
framework/behaviors/TimestampBehavior.php
View file @
d5bd6816
...
...
@@ -64,10 +64,12 @@ class TimestampBehavior extends AttributeBehavior
{
/**
* @var string the attribute that will receive timestamp value
* Set this property to be null if you do not want to record the creation time.
*/
public
$createdAtAttribute
=
'created_at'
;
/**
* @var string the attribute that will receive timestamp value
* @var string the attribute that will receive timestamp value.
* Set this property to be null if you do not want to record the update time.
*/
public
$updatedAtAttribute
=
'updated_at'
;
/**
...
...
framework/console/controllers/MessageController.php
View file @
d5bd6816
...
...
@@ -96,6 +96,9 @@ class MessageController extends Controller
if
(
!
is_dir
(
$config
[
'sourcePath'
]))
{
throw
new
Exception
(
"The source path
{
$config
[
'sourcePath'
]
}
is not a valid directory."
);
}
if
(
empty
(
$config
[
'format'
])
||
!
in_array
(
$config
[
'format'
],
[
'php'
,
'po'
,
'db'
]))
{
throw
new
Exception
(
'Format should be either "php", "po" or "db".'
);
}
if
(
in_array
(
$config
[
'format'
],
[
'php'
,
'po'
]))
{
if
(
!
isset
(
$config
[
'messagePath'
]))
{
throw
new
Exception
(
'The configuration file must specify "messagePath".'
);
...
...
@@ -106,9 +109,6 @@ class MessageController extends Controller
if
(
empty
(
$config
[
'languages'
]))
{
throw
new
Exception
(
"Languages cannot be empty."
);
}
if
(
empty
(
$config
[
'format'
])
||
!
in_array
(
$config
[
'format'
],
[
'php'
,
'po'
,
'db'
]))
{
throw
new
Exception
(
'Format should be either "php", "po" or "db".'
);
}
$files
=
FileHelper
::
findFiles
(
realpath
(
$config
[
'sourcePath'
]),
$config
);
...
...
framework/db/QueryBuilder.php
View file @
d5bd6816
...
...
@@ -868,7 +868,6 @@ class QueryBuilder extends \yii\base\Object
* on how to specify a condition.
* @param array $params the binding parameters to be populated
* @return string the generated SQL expression
* @throws InvalidParamException if the condition is in bad format
*/
public
function
buildCondition
(
$condition
,
&
$params
)
{
...
...
@@ -882,11 +881,11 @@ class QueryBuilder extends \yii\base\Object
$operator
=
strtoupper
(
$condition
[
0
]);
if
(
isset
(
$this
->
conditionBuilders
[
$operator
]))
{
$method
=
$this
->
conditionBuilders
[
$operator
];
array_shift
(
$condition
);
return
$this
->
$method
(
$operator
,
$condition
,
$params
);
}
else
{
throw
new
InvalidParamException
(
'Found unknown operator in query: '
.
$operator
)
;
$method
=
'buildSimpleCondition'
;
}
array_shift
(
$condition
);
return
$this
->
$method
(
$operator
,
$condition
,
$params
);
}
else
{
// hash format: 'column1' => 'value1', 'column2' => 'value2', ...
return
$this
->
buildHashCondition
(
$condition
,
$params
);
}
...
...
@@ -1194,4 +1193,29 @@ class QueryBuilder extends \yii\base\Object
throw
new
InvalidParamException
(
'Subquery for EXISTS operator must be a Query object.'
);
}
}
/**
* Creates an SQL expressions like `"column" operator value`.
* @param string $operator the operator to use. Anything could be used e.g. `>`, `<=`, etc.
* @param array $operands contains two column names.
* @param array $params the binding parameters to be populated
* @return string the generated SQL expression
*/
public
function
buildSimpleCondition
(
$operator
,
$operands
,
&
$params
)
{
if
(
count
(
$operands
)
!==
2
)
{
throw
new
InvalidParamException
(
"Operator '
$operator
' requires two operands."
);
}
list
(
$column
,
$value
)
=
$operands
;
if
(
strpos
(
$column
,
'('
)
===
false
)
{
$column
=
$this
->
db
->
quoteColumnName
(
$column
);
}
$phName
=
self
::
PARAM_PREFIX
.
count
(
$params
);
$params
[
$phName
]
=
$value
===
null
?
'NULL'
:
$value
;
return
"
$column
$operator
$phName
"
;
}
}
framework/db/QueryTrait.php
View file @
d5bd6816
...
...
@@ -253,20 +253,6 @@ trait QueryTrait
return
[];
}
break
;
case
'IN'
:
case
'NOT IN'
:
case
'LIKE'
:
case
'OR LIKE'
:
case
'NOT LIKE'
:
case
'OR NOT LIKE'
:
case
'ILIKE'
:
// PostgreSQL operator for case insensitive LIKE
case
'OR ILIKE'
:
case
'NOT ILIKE'
:
case
'OR NOT ILIKE'
:
if
(
array_key_exists
(
1
,
$condition
)
&&
$this
->
isEmpty
(
$condition
[
1
]))
{
return
[];
}
break
;
case
'BETWEEN'
:
case
'NOT BETWEEN'
:
if
(
array_key_exists
(
1
,
$condition
)
&&
array_key_exists
(
2
,
$condition
))
{
...
...
@@ -276,7 +262,9 @@ trait QueryTrait
}
break
;
default
:
throw
new
NotSupportedException
(
"Operator not supported:
$operator
"
);
if
(
array_key_exists
(
1
,
$condition
)
&&
$this
->
isEmpty
(
$condition
[
1
]))
{
return
[];
}
}
array_unshift
(
$condition
,
$operator
);
...
...
framework/validators/FileValidator.php
View file @
d5bd6816
...
...
@@ -122,7 +122,7 @@ class FileValidator extends Validator
* - {mimeTypes}: the value of [[mimeTypes]]
*/
public
$wrongMimeType
;
/**
* @inheritdoc
...
...
@@ -150,12 +150,16 @@ class FileValidator extends Validator
}
if
(
!
is_array
(
$this
->
extensions
))
{
$this
->
extensions
=
preg_split
(
'/[\s,]+/'
,
strtolower
(
$this
->
extensions
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
}
else
{
$this
->
extensions
=
array_map
(
'strtolower'
,
$this
->
extensions
);
}
if
(
$this
->
wrongMimeType
===
null
)
{
$this
->
wrongMimeType
=
Yii
::
t
(
'yii'
,
'Only files with these MIME types are allowed: {mimeTypes}.'
);
}
if
(
!
is_array
(
$this
->
mimeTypes
))
{
$this
->
mimeTypes
=
preg_split
(
'/[\s,]+/'
,
strtolower
(
$this
->
mimeTypes
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
}
else
{
$this
->
mimeTypes
=
array_map
(
'strtolower'
,
$this
->
mimeTypes
);
}
}
...
...
@@ -330,58 +334,77 @@ class FileValidator extends Validator
/**
* @inheritdoc
*/
public
function
clientValidateAttribute
(
$object
,
$attribute
,
$view
)
{
public
function
clientValidateAttribute
(
$object
,
$attribute
,
$view
)
{
ValidationAsset
::
register
(
$view
);
$options
=
$this
->
getClientOptions
(
$object
,
$attribute
);
return
'yii.validation.file(attribute, messages, '
.
json_encode
(
$options
)
.
');'
;
}
/**
* Returns the client side validation options.
* @param \yii\base\Model $object the model being validated
* @param string $attribute the attribute name being validated
* @return array the client side validation options
*/
protected
function
getClientOptions
(
$object
,
$attribute
)
{
$label
=
$object
->
getAttributeLabel
(
$attribute
);
if
(
$this
->
message
!==
null
){
$options
[
'message'
]
=
Yii
::
$app
->
getI18n
()
->
format
(
$this
->
message
,
[
'attribute'
=>
$label
,
],
Yii
::
$app
->
language
);
}
$options
[
'skipOnEmpty'
]
=
$this
->
skipOnEmpty
;
if
(
!
$this
->
skipOnEmpty
)
{
$options
[
'uploadRequired'
]
=
Yii
::
$app
->
getI18n
()
->
format
(
$this
->
uploadRequired
,
[],
Yii
::
$app
->
language
);
if
(
!
$this
->
skipOnEmpty
)
{
$options
[
'uploadRequired'
]
=
Yii
::
$app
->
getI18n
()
->
format
(
$this
->
uploadRequired
,
[
'attribute'
=>
$label
,
],
Yii
::
$app
->
language
);
}
if
(
$this
->
mimeTypes
!==
null
)
{
$options
[
'mimeTypes'
]
=
$this
->
mimeTypes
;
$options
[
'wrongMimeType'
]
=
Yii
::
$app
->
getI18n
()
->
format
(
$this
->
wrongMimeType
,
[
'attribute'
=>
$label
,
'mimeTypes'
=>
join
(
', '
,
$this
->
mimeTypes
)
],
Yii
::
$app
->
language
);
}
if
(
$this
->
extensions
!==
null
)
{
$options
[
'extensions'
]
=
$this
->
extensions
;
$options
[
'wrongExtension'
]
=
Yii
::
$app
->
getI18n
()
->
format
(
$this
->
wrongExtension
,
[
'attribute'
=>
$label
,
'extensions'
=>
join
(
', '
,
$this
->
extensions
)
],
Yii
::
$app
->
language
);
}
if
(
$this
->
minSize
!==
null
)
{
$options
[
'minSize'
]
=
$this
->
minSize
;
$options
[
'tooSmall'
]
=
Yii
::
$app
->
getI18n
()
->
format
(
$this
->
tooSmall
,
[
'attribute'
=>
$label
,
'limit'
=>
$this
->
minSize
],
Yii
::
$app
->
language
);
}
if
(
$this
->
maxSize
!==
null
)
{
$options
[
'maxSize'
]
=
$this
->
maxSize
;
$options
[
'tooBig'
]
=
Yii
::
$app
->
getI18n
()
->
format
(
$this
->
tooBig
,
[
'attribute'
=>
$label
,
'limit'
=>
$this
->
maxSize
],
Yii
::
$app
->
language
);
}
],
Yii
::
$app
->
language
);
}
if
(
$this
->
maxFiles
!==
null
)
{
$options
[
'maxFiles'
]
=
$this
->
maxFiles
;
$options
[
'tooMany'
]
=
Yii
::
$app
->
getI18n
()
->
format
(
$this
->
tooMany
,
[
'attribute'
=>
$label
,
'limit'
=>
$this
->
maxFiles
],
Yii
::
$app
->
language
);
}
ValidationAsset
::
register
(
$view
);
return
'yii.validation.file(value, messages, '
.
json_encode
(
$options
)
.
', attribute);'
;
return
$options
;
}
}
framework/validators/ImageValidator.php
View file @
d5bd6816
...
...
@@ -134,7 +134,7 @@ class ImageValidator extends FileValidator
return
[
$this
->
notImage
,
[
'file'
=>
$image
->
name
]];
}
list
(
$width
,
$height
,
$type
)
=
$imageInfo
;
list
(
$width
,
$height
)
=
$imageInfo
;
if
(
$width
==
0
||
$height
==
0
)
{
return
[
$this
->
notImage
,
[
'file'
=>
$image
->
name
]];
...
...
@@ -158,4 +158,64 @@ class ImageValidator extends FileValidator
return
null
;
}
/**
* @inheritdoc
*/
public
function
clientValidateAttribute
(
$object
,
$attribute
,
$view
)
{
ValidationAsset
::
register
(
$view
);
$options
=
$this
->
getClientOptions
(
$object
,
$attribute
);
return
'yii.validation.image(attribute, messages, '
.
json_encode
(
$options
)
.
', deferred);'
;
}
/**
* @inheritdoc
*/
protected
function
getClientOptions
(
$object
,
$attribute
)
{
$options
=
parent
::
getClientOptions
(
$object
,
$attribute
);
$label
=
$object
->
getAttributeLabel
(
$attribute
);
if
(
$this
->
notImage
!==
null
)
{
$options
[
'notImage'
]
=
Yii
::
$app
->
getI18n
()
->
format
(
$this
->
notImage
,
[
'attribute'
=>
$label
],
Yii
::
$app
->
language
);
}
if
(
$this
->
minWidth
!==
null
)
{
$options
[
'minWidth'
]
=
$this
->
minWidth
;
$options
[
'underWidth'
]
=
Yii
::
$app
->
getI18n
()
->
format
(
$this
->
underWidth
,
[
'attribute'
=>
$label
,
'limit'
=>
$this
->
minWidth
],
Yii
::
$app
->
language
);
}
if
(
$this
->
maxWidth
!==
null
)
{
$options
[
'maxWidth'
]
=
$this
->
maxWidth
;
$options
[
'overWidth'
]
=
Yii
::
$app
->
getI18n
()
->
format
(
$this
->
overWidth
,
[
'attribute'
=>
$label
,
'limit'
=>
$this
->
maxWidth
],
Yii
::
$app
->
language
);
}
if
(
$this
->
minHeight
!==
null
)
{
$options
[
'minHeight'
]
=
$this
->
minHeight
;
$options
[
'underHeight'
]
=
Yii
::
$app
->
getI18n
()
->
format
(
$this
->
underHeight
,
[
'attribute'
=>
$label
,
'limit'
=>
$this
->
maxHeight
],
Yii
::
$app
->
language
);
}
if
(
$this
->
maxHeight
!==
null
)
{
$options
[
'maxHeight'
]
=
$this
->
maxHeight
;
$options
[
'overHeight'
]
=
Yii
::
$app
->
getI18n
()
->
format
(
$this
->
overHeight
,
[
'attribute'
=>
$label
,
'limit'
=>
$this
->
maxHeight
],
Yii
::
$app
->
language
);
}
return
$options
;
}
}
tests/unit/data/base/Singer.php
View file @
d5bd6816
...
...
@@ -10,6 +10,7 @@ class Singer extends Model
{
public
$firstName
;
public
$lastName
;
public
$test
;
public
function
rules
()
{
...
...
@@ -17,6 +18,7 @@ class Singer extends Model
[[
'lastName'
],
'default'
,
'value'
=>
'Lennon'
],
[[
'lastName'
],
'required'
],
[[
'underscore_style'
],
'yii\captcha\CaptchaValidator'
],
[[
'test'
],
'required'
,
'when'
=>
function
(
$model
)
{
return
$model
->
firstName
===
'cebe'
;
}],
];
}
}
tests/unit/extensions/gii/GeneratorsTest.php
View file @
d5bd6816
<?php
namespace
yiiunit\extensions\gii
;
use
yii\gii\CodeFile
;
use
yii\gii\generators\controller\Generator
as
ControllerGenerator
;
use
yii\gii\generators\crud\Generator
as
CRUDGenerator
;
use
yii\gii\generators\extension\Generator
as
ExtensionGenerator
;
...
...
@@ -53,7 +54,11 @@ class GeneratorsTest extends GiiTestCase
$generator
->
modelClass
=
'Profile'
;
if
(
$generator
->
validate
())
{
$generator
->
generate
();
$files
=
$generator
->
generate
();
$modelCode
=
$files
[
0
]
->
content
;
$this
->
assertTrue
(
strpos
(
$modelCode
,
"'id' => 'ID'"
)
!==
false
,
"ID label should be there:
\n
"
.
$modelCode
);
$this
->
assertTrue
(
strpos
(
$modelCode
,
"'description' => 'Description',"
)
!==
false
,
"Description label should be there:
\n
"
.
$modelCode
);
}
else
{
print_r
(
$generator
->
getErrors
());
}
...
...
tests/unit/framework/base/ModelTest.php
View file @
d5bd6816
...
...
@@ -216,7 +216,7 @@ class ModelTest extends TestCase
public
function
testDefaultScenarios
()
{
$singer
=
new
Singer
();
$this
->
assertEquals
([
'default'
=>
[
'lastName'
,
'underscore_style'
]],
$singer
->
scenarios
());
$this
->
assertEquals
([
'default'
=>
[
'lastName'
,
'underscore_style'
,
'test'
]],
$singer
->
scenarios
());
$scenarios
=
[
'default'
=>
[
'id'
,
'name'
,
'description'
],
...
...
@@ -238,6 +238,13 @@ class ModelTest extends TestCase
$singer
=
new
Singer
();
$this
->
assertFalse
(
$singer
->
isAttributeRequired
(
'firstName'
));
$this
->
assertTrue
(
$singer
->
isAttributeRequired
(
'lastName'
));
// attribute is not marked as required when a conditional validation is applied using `$when`.
// the condition should not be applied because this info may be retrieved before model is loaded with data
$singer
->
firstName
=
'qiang'
;
$this
->
assertFalse
(
$singer
->
isAttributeRequired
(
'test'
));
$singer
->
firstName
=
'cebe'
;
$this
->
assertFalse
(
$singer
->
isAttributeRequired
(
'test'
));
}
public
function
testCreateValidators
()
...
...
tests/unit/framework/db/QueryBuilderTest.php
View file @
d5bd6816
...
...
@@ -152,10 +152,93 @@ class QueryBuilderTest extends DatabaseTestCase
[
[
'or like'
,
'name'
,
[
'heyho'
,
'abc'
]],
'"name" LIKE :qp0 OR "name" LIKE :qp1'
,
[
':qp0'
=>
'%heyho%'
,
':qp1'
=>
'%abc%'
]
],
[
[
'or not like'
,
'name'
,
[
'heyho'
,
'abc'
]],
'"name" NOT LIKE :qp0 OR "name" NOT LIKE :qp1'
,
[
':qp0'
=>
'%heyho%'
,
':qp1'
=>
'%abc%'
]
],
// TODO add more conditions
// IN
// NOT
// ...
// not
[
[
'not'
,
'name'
],
'NOT (name)'
,
[]
],
// and
[
[
'and'
,
'id=1'
,
'id=2'
],
'(id=1) AND (id=2)'
,
[]
],
[
[
'and'
,
'type=1'
,
[
'or'
,
'id=1'
,
'id=2'
]],
'(type=1) AND ((id=1) OR (id=2))'
,
[]
],
// or
[
[
'or'
,
'id=1'
,
'id=2'
],
'(id=1) OR (id=2)'
,
[]
],
[
[
'or'
,
'type=1'
,
[
'or'
,
'id=1'
,
'id=2'
]],
'(type=1) OR ((id=1) OR (id=2))'
,
[]
],
// between
[
[
'between'
,
'id'
,
1
,
10
],
'"id" BETWEEN :qp0 AND :qp1'
,
[
':qp0'
=>
1
,
':qp1'
=>
10
]
],
[
[
'not between'
,
'id'
,
1
,
10
],
'"id" NOT BETWEEN :qp0 AND :qp1'
,
[
':qp0'
=>
1
,
':qp1'
=>
10
]
],
// in
[
[
'in'
,
'id'
,
[
1
,
2
,
3
]],
'"id" IN (:qp0, :qp1, :qp2)'
,
[
':qp0'
=>
1
,
':qp1'
=>
2
,
':qp2'
=>
3
]
],
[
[
'not in'
,
'id'
,
[
1
,
2
,
3
]],
'"id" NOT IN (:qp0, :qp1, :qp2)'
,
[
':qp0'
=>
1
,
':qp1'
=>
2
,
':qp2'
=>
3
]
],
// TODO: exists and not exists
// simple conditions
[
[
'='
,
'a'
,
'b'
],
'"a" = :qp0'
,
[
':qp0'
=>
'b'
]
],
[
[
'>'
,
'a'
,
1
],
'"a" > :qp0'
,
[
':qp0'
=>
1
]
],
[
[
'>='
,
'a'
,
'b'
],
'"a" >= :qp0'
,
[
':qp0'
=>
'b'
]
],
[
[
'<'
,
'a'
,
2
],
'"a" < :qp0'
,
[
':qp0'
=>
2
]
],
[
[
'<='
,
'a'
,
'b'
],
'"a" <= :qp0'
,
[
':qp0'
=>
'b'
]
],
[
[
'<>'
,
'a'
,
3
],
'"a" <> :qp0'
,
[
':qp0'
=>
3
]
],
[
[
'!='
,
'a'
,
'b'
],
'"a" != :qp0'
,
[
':qp0'
=>
'b'
]
],
];
// adjust dbms specific escaping
foreach
(
$conditions
as
$i
=>
$condition
)
{
switch
(
$this
->
driverName
)
{
case
'mssql'
:
case
'mysql'
:
case
'sqlite'
:
$conditions
[
$i
][
1
]
=
str_replace
(
'"'
,
'`'
,
$condition
[
1
]);
break
;
}
}
return
$conditions
;
}
public
function
filterConditionProvider
()
{
$conditions
=
[
// like
[
[
'like'
,
'name'
,
[]],
''
,
[]
],
[
[
'not like'
,
'name'
,
[]],
''
,
[]
],
[
[
'or like'
,
'name'
,
[]],
''
,
[]
],
[
[
'or not like'
,
'name'
,
[]],
''
,
[]
],
// not
[
[
'not'
,
''
],
''
,
[]
],
// and
[
[
'and'
,
''
,
''
],
''
,
[]
],
[
[
'and'
,
''
,
'id=2'
],
'(id=2)'
,
[]
],
[
[
'and'
,
'id=1'
,
''
],
'(id=1)'
,
[]
],
[
[
'and'
,
'type=1'
,
[
'or'
,
''
,
'id=2'
]],
'(type=1) AND ((id=2))'
,
[]
],
// or
[
[
'or'
,
'id=1'
,
''
],
'(id=1)'
,
[]
],
[
[
'or'
,
'type=1'
,
[
'or'
,
''
,
'id=2'
]],
'(type=1) OR ((id=2))'
,
[]
],
// between
[
[
'between'
,
'id'
,
1
,
null
],
''
,
[]
],
[
[
'not between'
,
'id'
,
null
,
10
],
''
,
[]
],
// in
[
[
'in'
,
'id'
,
[]],
''
,
[]
],
[
[
'not in'
,
'id'
,
[]],
''
,
[]
],
// TODO: exists and not exists
// simple conditions
[
[
'='
,
'a'
,
''
],
''
,
[]
],
[
[
'>'
,
'a'
,
''
],
''
,
[]
],
[
[
'>='
,
'a'
,
''
],
''
,
[]
],
[
[
'<'
,
'a'
,
''
],
''
,
[]
],
[
[
'<='
,
'a'
,
''
],
''
,
[]
],
[
[
'<>'
,
'a'
,
''
],
''
,
[]
],
[
[
'!='
,
'a'
,
''
],
''
,
[]
],
];
// adjust dbms specific escaping
...
...
@@ -183,6 +266,17 @@ class QueryBuilderTest extends DatabaseTestCase
$this
->
assertEquals
(
'SELECT *'
.
(
empty
(
$expected
)
?
''
:
' WHERE '
.
$expected
),
$sql
);
}
/**
* @dataProvider filterConditionProvider
*/
public
function
testBuildFilterCondition
(
$condition
,
$expected
,
$expectedParams
)
{
$query
=
(
new
Query
())
->
filterWhere
(
$condition
);
list
(
$sql
,
$params
)
=
$this
->
getQueryBuilder
()
->
build
(
$query
);
$this
->
assertEquals
(
$expectedParams
,
$params
);
$this
->
assertEquals
(
'SELECT *'
.
(
empty
(
$expected
)
?
''
:
' WHERE '
.
$expected
),
$sql
);
}
public
function
testAddDropPrimaryKey
()
{
$tableName
=
'constraints'
;
...
...
tests/unit/framework/rbac/PhpManagerTest.php
View file @
d5bd6816
...
...
@@ -75,13 +75,13 @@ class PhpManagerTest extends ManagerTestCase
public
function
testSaveLoad
()
{
static
::
$filemtime
=
time
();
$this
->
prepareData
();
$items
=
$this
->
auth
->
items
;
$children
=
$this
->
auth
->
children
;
$assignments
=
$this
->
auth
->
assignments
;
$rules
=
$this
->
auth
->
rules
;
static
::
$filemtime
=
time
();
$this
->
auth
->
save
();
$this
->
auth
=
$this
->
createManager
();
...
...
tests/unit/framework/widgets/ActiveFieldTest.php
View file @
d5bd6816
...
...
@@ -266,7 +266,7 @@ EOD;
$this
->
activeField
->
model
->
addRule
(
$this
->
attributeName
,
'yiiunit\framework\widgets\TestValidator'
);
$this
->
activeField
->
enableClientValidation
=
true
;
$actualValue
=
$this
->
activeField
->
getClientOptions
();
$expectedJsExpression
=
"function (attribute, value, messages) {return true;}"
;
$expectedJsExpression
=
"function (attribute, value, messages
, deferred
) {return true;}"
;
$expectedValidateOnChange
=
true
;
$expectedValidateOnType
=
false
;
$expectedValidationDelay
=
200
;
...
...
@@ -286,7 +286,7 @@ EOD;
$this
->
activeField
->
enableAjaxValidation
=
true
;
$this
->
activeField
->
model
->
addRule
(
$this
->
attributeName
,
'yiiunit\framework\widgets\TestValidator'
);
$actualValue
=
$this
->
activeField
->
getClientOptions
();
$expectedJsExpression
=
"function (attribute, value, messages) {return true;}"
;
$expectedJsExpression
=
"function (attribute, value, messages
, deferred
) {return true;}"
;
$expectedValidateOnChange
=
true
;
$expectedValidateOnType
=
false
;
$expectedValidationDelay
=
200
;
...
...
@@ -313,7 +313,7 @@ EOD;
}
$actualValue
=
$this
->
activeField
->
getClientOptions
();
$expectedJsExpression
=
"function (attribute, value, messages) {if (function (attribute, value) "
$expectedJsExpression
=
"function (attribute, value, messages
, deferred
) {if (function (attribute, value) "
.
"{ return 'yii2' == 'yii2'; }(attribute, value)) { return true; }}"
;
$expectedValidateOnChange
=
true
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment