Commit e1c30eab by Tobias Munk

Merge commit 'b2a2853e' into feature/toolbar-ui-3

parents 4d616298 b2a2853e
......@@ -22,8 +22,8 @@ install:
- tests/unit/data/travis/cubrid-setup.sh
# basic application:
- composer install --dev --prefer-dist -d apps/basic
- cd apps/basic && composer require --dev "codeception/codeception: 1.8.*@dev" "codeception/specify: *" "codeception/verify: *"
- cd apps/basic && php vendor/bin/codecept build && cd ../..
- cd apps/basic && composer require --dev codeception/codeception:1.8.*@dev codeception/specify:* codeception/verify:*
- php vendor/bin/codecept build && cd ../..
- cd apps && php -S localhost:8080 &
before_script:
......
......@@ -4,7 +4,7 @@ return [
'debug',
],
'modules' => [
'debug' => 'yii\debug\Module',
'gii' => 'yii\gii\Module',
'debug' => 'yii\debug\Module',
'gii' => 'yii\gii\Module',
],
];
......@@ -25,9 +25,9 @@
"yiisoft/yii2-gii": "*"
},
"suggest": {
"codeception/codeception": "1.8.*@dev",
"codeception/specify": "*",
"codeception/verify": "*"
"codeception/codeception": "Codeception, 1.8.*@dev is currently works well with Yii.",
"codeception/specify": "BDD style code blocks for PHPUnit and Codeception",
"codeception/verify": "BDD Assertions for PHPUnit and Codeception"
},
"scripts": {
"post-create-project-cmd": [
......
......@@ -20,6 +20,7 @@ $config = [
],
'mail' => [
'class' => 'yii\swiftmailer\Mailer',
'useFileTransport' => true,
],
'log' => [
'traceLevel' => YII_DEBUG ? 3 : 0,
......
......@@ -20,6 +20,17 @@ $this->params['breadcrumbs'][] = $this->title;
Thank you for contacting us. We will respond to you as soon as possible.
</div>
<p>
Note that if you turn on the Yii debugger, you should be able
to view the mail message on the mail panel of the debugger.
<?php if (Yii::$app->mail->useFileTransport): ?>
Because the application is in development mode, the email is not sent but saved as
a file under <code><?= Yii::getAlias(Yii::$app->mail->fileTransportPath); ?></code>.
Please configure the <code>useFileTransport</code> property of the <code>mail</code>
application component to be false to enable email sending.
<?php endif; ?>
</p>
<?php else: ?>
<p>
......
......@@ -369,7 +369,11 @@ foreach ($customers as $customer) {
}
```
As you can see, only two SQL queries are needed for the same task.
As you can see, only two SQL queries are needed for the same task!
> Info: In general, if you are eager loading `N` relations among which `M` relations are defined with `via()` or `viaTable()`,
> a total number of `1+M+N` SQL queries will be performed: one query to bring back the rows for the primary table, one for
> each of the `M` pivot tables corresponding to the `via()` or `viaTable()` calls, and one for each of the `N` related tables.
Sometimes, you may want to customize the relational queries on the fly. This can be
......
......@@ -3,35 +3,42 @@ Configuration
Yii applications rely upon components to perform most of the common tasks, such as connecting to a database, routing browser
requests, and handling sessions. How these stock components behave can be adjusted by *configuring* your Yii application.
The majority of components have sensible defaults, so it's unlikely that you'll spend a lot of time configuring
them. Still there are some mandatory settings, such as the database connection, that you will have to establish.
The majority of components have sensible default settings, so it's unlikely that you'll do a lot of configuration. Still, there are some mandatory configuration settings that you will have to establish, such as the database connection.
How an application is configured depends on application template but there are some general principles applying in any case.
How an application is configured depends upon the application template in use, but there are some general principles that apply in every Yii case.
Configuring options in bootstrap file
-------------------------------------
Configuring options in the bootstrap file
-----------------------------------------
For each application in Yii there is at least one bootstrap file. For web applications it's typically `index.php`, for
console applications it's `yii`. Both are doing nearly the same job:
For each application in Yii there is at least one bootstrap file: a PHP script through which all requests are handled. For web applications, the bootstrap file is typically `index.php`; for
console applications, the bootstrap file is `yii`. Both bootstrap files perform nearly the same job:
1. Setting common constants.
2. Including Yii itself.
2. Including the Yii framework itself.
3. Including [Composer autoloader](http://getcomposer.org/doc/01-basic-usage.md#autoloading).
4. Reading config file into `$config`.
5. Creating new application instance using `$config` and running it.
4. Reading the configuration file into `$config`.
5. Creating a new application instance, configured via `$config`, and running that instance.
The Bootstrap file is not the part of framework but your application so it's OK to adjust it to fit your application. Typical
adjustments are the value of `YII_DEBUG` that should never be `true` on production and the way config is read:
Like any resource in your Yii application, the bootstrap file can be edited to fit your needs. A typical change is to the value of `YII_DEBUG`. This constant should be `true` during development, but always `false` on production sites.
The default bootstrap structure sets `YII_DEBUG` to `false` if not defined:
```php
defined('YII_DEBUG') or define('YII_DEBUG', false);
```
During development, you can change this to `true`:
```php
define('YII_DEBUG', true); // Development only
defined('YII_DEBUG') or define('YII_DEBUG', false);
```
Configuring application instance
--------------------------------
Configuring the application instance
------------------------------------
It was mentioned above that application is configured in bootstrap file when its instance is created. Config is typically
stored in a PHP file in the `/config` directory of the application and looks like the following:
An application instance is configured when it's created in the bootstrap file. The configuration is typically
stored in a PHP file stored in the `/config` application directory. The file has this structure to begin:
```php
<?php
......@@ -45,18 +52,18 @@ return [
];
```
In the above array keys are names of application properties. Depending on application type you can check properties of
either [[yii\web\Application]] or [[yii\console\Application]]. Both are extended from [[yii\base\Application]].
The configuration is a large array of key-value pairs. In the above, the array keys are the names of application properties. Depending upon the application type, you can configure the properties of
either [[yii\web\Application]] or [[yii\console\Application]] classes. Both classes extend [[yii\base\Application]].
> Note that you can configure not only public class properties but anything accessible via setter. For example, to
configure runtime path you can use key named `runtimePath`. There's no such property in the application class but
since there's a corresponding setter named `setRuntimePath` it will be properly configured.
This feature is added to any class that extends from [[yii\base\Object]] which is nearly any class of the Yii framework.
Note that you can configure not only public class properties, but any property accessible via a setter. For example, to
configure the runtime path, you can use a key named `runtimePath`. There's no such property in the application class, but
since the class has a corresponding setter named `setRuntimePath`, `runtimePath` becomes configurable.
The ability to configure properties via setters is available to any class that extends from [[yii\base\Object]], which is nearly every class in the Yii framework.
Configuring application components
----------------------------------
Majority of Yii functionality are application components. These are attached to application via its `components` property:
The majority of the Yii functionality comes from application components. These components are attached to the application instance via the instance's `components` property:
```php
<?php
......@@ -81,23 +88,21 @@ return [
];
```
In the above four components are configured: `cache`, `user`, `errorHandler`, `log`. Each entry key is a component ID
and the value is the configuration array. ID is used to access the component like `\Yii::$app->myComponent`.
Configuration array has one special key named `class` that sets the component class. The rest of the keys and values are used
to configure component properties in the same way as top-level keys are used to configure application properties.
In the above code, four components are configured: `cache`, `user`, `errorHandler`, `log`. Each entry's key is a component ID. The values are subarrays used to configure that component. The component ID is also used to access the component anywhere within the application, using code like `\Yii::$app->myComponent`.
The configuration array has one special key named `class` that identifies the component's base class. The rest of the keys and values are used
to configure component properties in the same way as top-level keys are used to configure the application's properties.
Each application has a predefined set of components. In case of configuring one of these, the `class` key is omitted and
application default class is used instead. You can check `registerCoreComponents()` method of the application you are using
Each application has a predefined set of components. To configure one of these, the `class` key can be omitted to use the default Yii class for that component. You can check the `registerCoreComponents()` method of the application you are using
to get a list of component IDs and corresponding classes.
Note that Yii is smart enough to configure the component when it's actually used i.e. if `cache` is never used it will
not be instantiated and configured at all.
Note that Yii is smart enough to only configure the component when it's actually being used: for example, if you configure the `cache` component in your configuration file but never use the `cache` component in your code, no instance of that component will be created and no time is wasted configuring it.
Setting component defaults classwide
------------------------------------
For each component you can specifiy classwide defaults. For example, if we want to change class for all `LinkPager`
widgets without specifying it over and over again when widget is called we can do it like the following:
For each component you can specifiy classwide defaults. For example, if you want to change the class used for all `LinkPager`
widgets without specifying the class for every widget usage, you can do the following:
```php
\Yii::$objectConfig = [
......@@ -109,5 +114,5 @@ widgets without specifying it over and over again when widget is called we can d
];
```
The code above should be executed once before `LinkPager` widget is used. It can be done in `index.php`, application
config or anywhere else.
\ No newline at end of file
The code above should be executed once before `LinkPager` widget is used. It can be done in `index.php`, the application
configuration file, or anywhere else.
\ No newline at end of file
......@@ -58,16 +58,27 @@ exit($exitCode);
```
This script is a part of your application so you're free to adjust it. The `YII_DEBUG` constant can be set `false` if you do
not want to see stacktrace on error and want to improve overall performance. In both basic and advanced application
not want to see stack trace on error and want to improve overall performance. In both basic and advanced application
templates it is enabled to provide more developer-friendly environment.
Configuration
-------------
As can be seen in the code above, console application uses its own config files named `console.php` so you need to
configure database connection and the rest of the components you're going to use there in that file. If web and console
application configs have a lot in common it's a good idea to move matching parts into their own config files as it was
done in advanced application template.
As can be seen in the code above, console application uses its own config files named `console.php`. In this file,
you should specify how to configure various application components and properties.
If your Web application and the console application share a lot of configurations, you may consider moving the common
part into a separate file, and include this file in both of the application configurations, just as what is done
in the "advanced" application template.
Sometimes, you may want to run a console command using an application configuration that is different from the one
specified in the entry script. For example, you may want to use the `yii migrate` command to upgrade your
test databases which are configured in each individual test suite. To do so, simply specify the custom application configuration
file via the `appconfig` option, like the following,
```
yii <route> --appconfig=path/to/config.php ...
```
Creating your own console commands
......
Error Handling
==============
Error handling in Yii is different from plain PHP. First of all, all non-fatal errors are converted to exceptions so
you can use `try`-`catch` to work with these. Second, even fatal errors are rendered in a nice way. In debug mode that
means you have a trace and a piece of code where it happened so it takes less time to analyze and fix it.
Error handling in Yii is different than handling errors in plain PHP. First of all, Yii will convert all non-fatal errors to *exceptions*. By doing so, you can gracefully handle them using `try`-`catch`. Second, even fatal errors in Yii are rendered in a nice way. This means that in debugging mode, you can trace the causes of fatal errors in order to more quickly identify the cause of the problem.
Using controller action to render errors
----------------------------------------
Rendering errors in a dedicated controller action
-------------------------------------------------
Default Yii error page is great for development mode and is OK for production if `YII_DEBUG` is turned off but you may
have an idea how to make it more suitable for your project. An easiest way to customize it is to use controller action
for error rendering. In order to do so you need to configure `errorHandler` component via application config:
The default Yii error page is great when developing a site, and is acceptable for production sites if `YII_DEBUG` is turned off in your bootstrap index.php file. But but you may want to customize the default error page to make it more suitable for your project.
The easiest way to create a custom error page it is to use a dedicated controller action for error rendering. First, you'll need to configure the `errorHandler` component in the application's configuration:
```php
return [
......@@ -22,7 +20,7 @@ return [
],
```
After it is done in case of error, Yii will launch `SiteController::actionError()`:
With that configuration in place, whenever an error occurs, Yii will execute the "error" action of the "Site" controller. That action should look for an exception and, if present, render the proper view file, passing along the exception:
```php
public function actionError()
......@@ -33,8 +31,22 @@ public function actionError()
}
```
Since most of the time you need to adjust look and feel only, Yii provides `ErrorAction` class that can be used in
controller instead of implementing action yourself:
Next, you would create the `views/site/error.php` file, which would make use of the exception. The exception object has the following properties:
* `code`: the HTTP status code (e.g. 403, 500)
* `type`: the error type (e.g. CHttpException, PHP Error)
* `message`: the error message
* `file`: the name of the PHP script file where the error occurs
* `line`: the line number of the code where the error occurs
* `trace`: the call stack of the error
* `source`: the context source code where the error occurs
[[Need to confirm the above for Yii 2.]]
Rendering errors without a dedicated controller action
------------------------------------------------------
Instead of creating a dedicated action within the Site controller, you could just indicate to Yii what class should be used to handle errors:
```php
public function actions()
......@@ -47,10 +59,10 @@ public function actions()
}
```
After defining `actions` in `SiteController` as shown above you can create `views/site/error.php`. In the view there
are three variables available:
After associating the class with the error as in the above, define the `views/site/error.php` file, which will automatically be used. The view will be passed three variables:
- `$name`: the error name
- `$message`: the error message
- `$exception`: the exception being handled
The `$exception` object will have the same properties outlined above.
Using template engines
======================
By default Yii uses PHP as template language, but you can configure it to support other rendering engines, such as
By default, Yii uses PHP as its template language, but you can configure Yii to support other rendering engines, such as
[Twig](http://twig.sensiolabs.org/) or [Smarty](http://www.smarty.net/).
The `view` component is responsible for rendering views. You can add a custom template engines by reconfiguring this
The `view` component is responsible for rendering views. You can add a custom template engine by reconfiguring this
component's behavior:
```php
......@@ -30,22 +30,21 @@ component's behavior:
]
```
In the config above we're using Smarty and Twig. In order to get these extensions in your project you need to modify
your `composer.json` to include
In the code above, both Smarty and Twig are configured to be useable by the view files. But in order to get these extensions into your project, you need to also modify
your `composer.json` file to include them, too:
```
"yiisoft/yii2-smarty": "*",
"yiisoft/yii2-twig": "*",
```
in `require` section and then run `composer update --preder-dist`.
That code would be added to the `require` section of `composer.json`. After making that change and saving the file, you can install the extensions by running `composer update --preder-dist` in the command-line.
Twig
----
To use Twig, you need to create templates in files with the `.twig` extension (or use another file extension but configure the component accordingly).
Unlike standard view files, when using Twig, you must include the extension when calling `$this->render()`
or `$this->renderPartial()` from your controller:
To use Twig, you need to create templates in files that have the `.twig` extension (or use another file extension but configure the component accordingly).
Unlike standard view files, when using Twig you must include the extension in your `$this->render()`
or `$this->renderPartial()` controller calls:
```php
echo $this->render('renderer.twig', ['username' => 'Alex']);
......@@ -70,8 +69,8 @@ Within Twig templates, you can also make use of these variables:
### Globals
You can add global helpers or values via config's `globals`. It allows both using Yii helpers and setting your own
values:
You can add global helpers or values via the application configuration's `globals` variable. You can define both Yii helpers and your own
variables there:
```php
'globals' => [
......@@ -80,7 +79,7 @@ values:
],
```
Then in your template you can use it the following way:
Once configured, in your template you can use the globals in the following way:
```
Hello, {{name}}! {{ html.a('Please login', 'site/login') | raw }}.
......@@ -88,7 +87,7 @@ Hello, {{name}}! {{ html.a('Please login', 'site/login') | raw }}.
### Additional filters
Additional filters may be added via config's `filters` option:
Additional filters may be added via the application configuration's `filters` option:
```php
'filters' => [
......@@ -96,7 +95,7 @@ Additional filters may be added via config's `filters` option:
],
```
Then in the template you can use
Then in the template you can use:
```
{{ model|jsonEncode }}
......@@ -106,8 +105,8 @@ Then in the template you can use
Smarty
------
To use Smarty, you need to create templates in files with the `.tpl` extension (or use another file extension but configure the component accordingly). Unlike standard view files, when using Smarty, you must include the extension when calling `$this->render()`
or `$this->renderPartial()` from your controller:
To use Smarty, you need to create templates in files that have the `.tpl` extension (or use another file extension but configure the component accordingly). Unlike standard view files, when using Smarty you must include the extension in your `$this->render()`
or `$this->renderPartial()` controller calls:
```php
echo $this->render('renderer.tpl', ['username' => 'Alex']);
......
......@@ -4,7 +4,9 @@ Yii Framework 2 bootstrap extension Change Log
2.0.0 beta under development
----------------------------
- Bug #2361: `yii\bootstrap\NavBar::brandUrl` should default to the home URL of application (qiangxue)
- Enh #1474: Added option to make NavBar 100% width (cebe)
- Enh #1552: It is now possible to use multiple bootstrap NavBar in a single page (Alex-Code)
- Enh #1553: Only add navbar-default class to NavBar when no other class is specified (cebe)
- Enh #1601: Added support for tagName and encodeLabel parameters in ButtonDropdown (omnilight)
- Chg #1459: Update Collapse to use bootstrap 3 classes (tonydspaniard)
......
......@@ -7,6 +7,7 @@
namespace yii\bootstrap;
use Yii;
use yii\helpers\Html;
/**
......@@ -43,9 +44,9 @@ class NavBar extends Widget
public $brandLabel;
/**
* @param array|string $url the URL for the brand's hyperlink tag. This parameter will be processed by [[Html::url()]]
* and will be used for the "href" attribute of the brand link. Defaults to site root.
* and will be used for the "href" attribute of the brand link. If not set, [[\yii\web\Application::homeUrl]] will be used.
*/
public $brandUrl = '/';
public $brandUrl;
/**
* @var array the HTML attributes of the brand link.
*/
......@@ -84,7 +85,7 @@ class NavBar extends Widget
echo Html::beginTag('div', ['class' => 'navbar-header']);
echo $this->renderToggleButton();
if ($this->brandLabel !== null) {
echo Html::a($this->brandLabel, $this->brandUrl, $this->brandOptions);
echo Html::a($this->brandLabel, $this->brandUrl === null ? Yii::$app->homeUrl : $this->brandUrl, $this->brandOptions);
}
echo Html::endTag('div');
......
......@@ -283,6 +283,7 @@ class ActiveRecord extends BaseActiveRecord
* Creates an active record instance.
*
* This method is called together with [[populateRecord()]] by [[ActiveQuery]].
* It is not meant to be used for creating new records directly.
*
* You may override this method if the instance being created
* depends on the row data to be populated into the record.
......
......@@ -521,7 +521,8 @@ class Collection extends Object
* and the map values) and does the aggregation.
* Argument will be automatically cast to [[\MongoCode]].
* @param string|array $out output collection name. It could be a string for simple output
* ('outputCollection'), or an array for parametrized output (['merge' => 'outputCollection'])
* ('outputCollection'), or an array for parametrized output (['merge' => 'outputCollection']).
* You can pass ['inline' => true] to fetch the result at once without temporary collection usage.
* @param array $condition criteria for including a document in the aggregation.
* @param array $options additional optional parameters to the mapReduce command. Valid options include:
* - sort - array - key to sort the input documents. The sort key must be in an existing index for this collection.
......@@ -530,7 +531,7 @@ class Collection extends Object
* - scope - array - specifies global variables that are accessible in the map, reduce and finalize functions.
* - jsMode - boolean -Specifies whether to convert intermediate data into BSON format between the execution of the map and reduce functions.
* - verbose - boolean - specifies whether to include the timing information in the result information.
* @return string the map reduce output collection name.
* @return string|array the map reduce output collection name or output results.
* @throws Exception on failure.
*/
public function mapReduce($map, $reduce, $out, $condition = [], $options = [])
......@@ -566,7 +567,7 @@ class Collection extends Object
$result = $this->mongoCollection->db->command($command);
$this->tryResultError($result);
Yii::endProfile($token, __METHOD__);
return $result['result'];
return array_key_exists('results', $result) ? $result['results'] : $result['result'];
} catch (\Exception $e) {
Yii::endProfile($token, __METHOD__);
throw new Exception($e->getMessage(), (int)$e->getCode(), $e);
......
......@@ -3,7 +3,6 @@ Yii Framework 2 Change Log
2.0.0 beta under development
----------------------------
- 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)
......@@ -41,6 +40,7 @@ Yii Framework 2 Change Log
- Bug #2084: AssetController adjusting CSS URLs declared at same line fixed (klimov-paul)
- Bug #2091: `QueryBuilder::buildInCondition()` fails to handle array not starting with index 0 (qiangxue)
- Bug #2160: SphinxQL does not support OFFSET (qiangxue, romeo7)
- Bug #2209: When I18N message translation is missing source language is now used for formatting (samdark)
- Bug #2212: `yii\gridview\DataColumn` generates incorrect labels when used with nosql DB and there is no data (qiangxue)
- Bug #2298: Fixed the bug that Gii controller generator did not allow digit in the controller ID (qiangxue)
- Bug #2303: Fixed the bug that `yii\base\Theme::pathMap` did not support dynamic update with path aliases (qiangxue)
......@@ -66,7 +66,6 @@ Yii Framework 2 Change Log
- Enh #1476: Add yii\web\Session::handler property (nineinchnick)
- Enh #1499: Added `ActionColumn::controller` property to support customizing the controller for handling GridView actions (qiangxue)
- Enh #1523: Query conditions now allow to use the NOT operator (cebe)
- Enh #1552: It is now possible to use multiple bootstrap NavBar in a single page (Alex-Code)
- Enh #1572: Added `yii\web\Controller::createAbsoluteUrl()` (samdark)
- Enh #1579: throw exception when the given AR relation name does not match in a case sensitive manner (qiangxue)
- Enh #1581: Added `ActiveQuery::joinWith()` and `ActiveQuery::innerJoinWith()` to support joining with relations (qiangxue)
......@@ -92,6 +91,7 @@ Yii Framework 2 Change Log
- Enh #2008: `yii message/extract` is now able to save translation strings to database (kate-kate, samdark)
- Enh #2043: Added support for custom request body parsers (danschmidt5189, cebe)
- Enh #2051: Do not save null data into database when using RBAC (qiangxue)
- Enh #2054: Added support for using custom application configuration with the console command runner (qiangxue)
- Enh #2079:
- i18n now falls back to `en` from `en-US` if message translation isn't found (samdark)
- View now falls back to `en` from `en-US` if file not found (samdark)
......@@ -124,6 +124,7 @@ Yii Framework 2 Change Log
- 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)
- Enh #2325: Adding support for the `X-HTTP-Method-Override` header in `yii\web\Request::getMethod()` (pawzar)
- Enh #2364: Take into account current error reporting level in error handler (gureedo)
- Chg #1519: `yii\web\User::loginRequired()` now returns the `Response` object instead of exiting the application (qiangxue)
- Chg #1586: `QueryBuilder::buildLikeCondition()` will now escape special characters and use percentage characters by default (qiangxue)
- Chg #1610: `Html::activeCheckboxList()` and `Html::activeRadioList()` will submit an empty string if no checkbox/radio is selected (qiangxue)
......@@ -164,6 +165,7 @@ Yii Framework 2 Change Log
- Chg: Removed implementation of `Arrayable` from `yii\Object` (qiangxue)
- Chg: Renamed `ActiveRecordInterface::createActiveRelation()` to `createRelation()` (qiangxue)
- Chg: The scripts in asset bundles are now registered in `View` at the end of `endBody()`. It was done in `endPage()` previously (qiangxue)
- Chg: Renamed `csrf-var` to `csrf-param` for CSRF header name (Dilip)
- New #66: [Auth client library](https://github.com/yiisoft/yii2-authclient) OpenId, OAuth1, OAuth2 clients (klimov-paul)
- New #706: Added `yii\widgets\Pjax` and enhanced `GridView` to work with `Pjax` to support AJAX-update (qiangxue)
- New #1393: [Codeception testing framework integration](https://github.com/yiisoft/yii2-codeception) (Ragazzo)
......
......@@ -61,7 +61,7 @@ yii = (function ($) {
* @return string|undefined the CSRF variable name. Undefined is returned if CSRF validation is not enabled.
*/
getCsrfVar: function () {
return $('meta[name=csrf-var]').prop('content');
return $('meta[name=csrf-param]').prop('content');
},
/**
......
......@@ -247,7 +247,7 @@ abstract class Application extends Module
if (YII_ENABLE_ERROR_HANDLER) {
ini_set('display_errors', 0);
set_exception_handler([$this, 'handleException']);
set_error_handler([$this, 'handleError'], error_reporting());
set_error_handler([$this, 'handleError']);
if ($this->memoryReserveSize > 0) {
$this->_memoryReserve = str_repeat('x', $this->memoryReserveSize);
}
......@@ -551,7 +551,7 @@ abstract class Application extends Module
*/
public function handleError($code, $message, $file, $line)
{
if (error_reporting() !== 0) {
if (error_reporting() & $code) {
// load ErrorException manually here because autoloading them will not work
// when error occurs while autoloading a class
if (!class_exists('\\yii\\base\\Exception', false)) {
......
......@@ -174,9 +174,7 @@ class Component extends Object
}
}
}
if (method_exists($this, 'get' . $name)) {
throw new InvalidCallException('Unsetting read-only property: ' . get_class($this) . '.' . $name);
}
throw new InvalidCallException('Unsetting an unknown or read-only property: ' . get_class($this) . '.' . $name);
}
/**
......
......@@ -93,7 +93,7 @@ class CaptchaValidator extends Validator
'hashKey' => 'yiiCaptcha/' . $this->captchaAction,
'caseSensitive' => $this->caseSensitive,
'message' => strtr($this->message, [
'{attribute}' => $object->getAttributeLabel($attribute),
'attribute' => $object->getAttributeLabel($attribute),
]),
];
if ($this->skipOnEmpty) {
......
......@@ -54,6 +54,11 @@ use yii\base\InvalidRouteException;
class Application extends \yii\base\Application
{
/**
* The option name for specifying the application configuration file path.
*/
const OPTION_APPCONFIG = 'appconfig';
/**
* @var string the default route of this application. Defaults to 'help',
* meaning the `help` command.
*/
......@@ -68,6 +73,42 @@ class Application extends \yii\base\Application
*/
public $controller;
/**
* @inheritdoc
*/
public function __construct($config = [])
{
$config = $this->loadConfig($config);
parent::__construct($config);
}
/**
* Loads the configuration.
* This method will check if the command line option [[OPTION_APPCONFIG]] is specified.
* If so, the corresponding file will be loaded as the application configuration.
* Otherwise, the configuration provided as the parameter will be returned back.
* @param array $config the configuration provided in the constructor.
* @return array the actual configuration to be used by the application.
*/
protected function loadConfig($config)
{
if (!empty($_SERVER['argv'])) {
$option = '--' . self::OPTION_APPCONFIG . '=';
foreach ($_SERVER['argv'] as $param) {
if (strpos($param, $option) !== false) {
$path = substr($param, strlen($option));
if (!empty($path) && is_file($file = Yii::getAlias($path))) {
return require($file);
} else {
die("The configuration file does not exist: $path\n");
}
}
}
}
return $config;
}
/**
* Initialize the application.
*/
......
......@@ -66,7 +66,9 @@ class Request extends \yii\base\Request
foreach ($rawParams as $param) {
if (preg_match('/^--(\w+)(=(.*))?$/', $param, $matches)) {
$name = $matches[1];
$params[$name] = isset($matches[3]) ? $matches[3] : true;
if ($name !== Application::OPTION_APPCONFIG) {
$params[$name] = isset($matches[3]) ? $matches[3] : true;
}
} else {
$params[] = $param;
}
......
......@@ -16,32 +16,15 @@ use yii\test\FixtureTrait;
/**
* This command manages loading and unloading fixtures.
* You can specify different options of this command to point fixture manager
* to the specific tables of the different database connections.
*
* To use this command simply configure your console.php config like this:
*
* ~~~
* 'db' => [
* 'class' => 'yii\db\Connection',
* 'dsn' => 'mysql:host=localhost;dbname={your_database}',
* 'username' => '{your_db_user}',
* 'password' => '',
* 'charset' => 'utf8',
* ],
* ~~~
*
* ~~~
* #load fixtures under $fixturePath from UsersFixture class with default namespace "tests\unit\fixtures"
* #load fixtures from UsersFixture class with default namespace "tests\unit\fixtures"
* yii fixture/load User
*
* #also a short version of this command (generate action is default)
* yii fixture User
*
* #load fixtures under $fixturePath with the different database connection
* yii fixture/load User --db=someOtherDbConnection
*
* #load fixtures under different $fixturePath.
* #load fixtures with different namespace.
* yii fixture/load User --namespace=alias\my\custom\namespace\goes\here
* ~~~
*
......@@ -61,11 +44,7 @@ class FixtureController extends Controller
/**
* @var string controller default action ID.
*/
public $defaultAction = 'apply';
/**
* @var string id of the database connection component of the application.
*/
public $db = 'db';
public $defaultAction = 'load';
/**
* @var string default namespace to search fixtures in
*/
......@@ -85,7 +64,7 @@ class FixtureController extends Controller
public function globalOptions()
{
return array_merge(parent::globalOptions(), [
'db', 'namespace','globalFixtures'
'namespace','globalFixtures'
]);
}
......@@ -126,23 +105,16 @@ class FixtureController extends Controller
throw new Exception('No fixtures were found in namespace: "' . $this->namespace . '"' . '');
}
$transaction = $this->getDbConnection()->beginTransaction();
try {
$this->loadFixtures($this->createFixtures($fixtures));
$transaction->commit();
} catch (\Exception $e) {
$transaction->rollback();
$this->stdout("Exception occurred, transaction rollback. Tables will be in same state.\n", Console::BG_RED);
throw $e;
}
$fixturesObjects = $this->createFixtures($fixtures);
$this->unloadFixtures($fixturesObjects);
$this->loadFixtures($fixturesObjects);
$this->notifyLoaded($fixtures);
}
/**
* Unloads given fixtures. You can clear environment and unload multiple fixtures by specifying
* their names separated with commas, like: User,UserProfile,MyCustom. Be sure there is no
* whitespace between tables names.
* whitespace between names.
* @param array|string $fixtures
* @param array|string $except
*/
......@@ -169,43 +141,17 @@ class FixtureController extends Controller
}
$filtered = array_diff($foundFixtures, $except);
$fixtures = $this->getFixturesConfig(array_merge($this->globalFixtures ,$filtered));
$fixtures = $this->getFixturesConfig(array_merge($this->globalFixtures, $filtered));
if (!$fixtures) {
throw new Exception('No fixtures were found in namespace: ' . $this->namespace . '".');
}
$transaction = $this->getDbConnection()->beginTransaction();
try {
$this->unloadFixtures($this->createFixtures($fixtures));
$transaction->commit();
} catch (\Exception $e) {
$transaction->rollback();
$this->stdout("Exception occurred, transaction rollback. Tables will be in same state.\n", Console::BG_RED);
throw $e;
}
$this->unloadFixtures($this->createFixtures($fixtures));
$this->notifyUnloaded($fixtures);
}
/**
* Returns database connection component
* @return \yii\db\Connection
* @throws \yii\console\Exception if [[db]] is invalid.
*/
public function getDbConnection()
{
$db = Yii::$app->getComponent($this->db);
if ($db === null) {
throw new Exception("There is no database connection component with id \"{$this->db}\".");
}
return $db;
}
/**
* Notifies user that fixtures were successfully loaded.
* @param array $fixtures
*/
......
......@@ -1014,6 +1014,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
* Creates an active record instance.
*
* This method is called together with [[populateRecord()]] by [[ActiveQuery]].
* It is not meant to be used for creating new records directly.
*
* You may override this method if the instance being created
* depends on the row data to be populated into the record.
......
......@@ -279,13 +279,14 @@ class GridView extends BaseListView
/** @var Column $column */
$cells[] = $column->renderHeaderCell();
}
$content = implode('', $cells);
$content = Html::tag('tr', implode('', $cells), $this->headerRowOptions);
if ($this->filterPosition == self::FILTER_POS_HEADER) {
$content = $this->renderFilters() . $content;
} elseif ($this->filterPosition == self::FILTER_POS_BODY) {
$content .= $this->renderFilters();
}
return "<thead>\n" . Html::tag('tr', $content, $this->headerRowOptions) . "\n</thead>";
return "<thead>\n" . $content . "\n</thead>";
}
/**
......@@ -299,11 +300,11 @@ class GridView extends BaseListView
/** @var Column $column */
$cells[] = $column->renderFooterCell();
}
$content = implode('', $cells);
$content = Html::tag('tr', implode('', $cells), $this->footerRowOptions);
if ($this->filterPosition == self::FILTER_POS_FOOTER) {
$content .= $this->renderFilters();
}
return "<tfoot>\n" . Html::tag('tr', $content, $this->footerRowOptions) . "\n</tfoot>";
return "<tfoot>\n" . $content . "\n</tfoot>";
}
/**
......
......@@ -85,8 +85,13 @@ class I18N extends Component
*/
public function translate($category, $message, $params, $language)
{
$message = $this->getMessageSource($category)->translate($category, $message, $language);
return $this->format($message, $params, $language);
$messageSource = $this->getMessageSource($category);
$translation = $messageSource->translate($category, $message, $language);
if ($translation === false) {
return $this->format($message, $params, $messageSource->sourceLanguage);
} else {
return $this->format($translation, $params, $language);
}
}
/**
......
......@@ -78,14 +78,14 @@ class MessageSource extends Component
* @param string $category the message category
* @param string $message the message to be translated
* @param string $language the target language
* @return string the translated message (or the original message if translation is not needed)
* @return string|boolean the translated message or false if translation wasn't found or isn't required
*/
public function translate($category, $message, $language)
{
if ($this->forceTranslation || $language !== $this->sourceLanguage) {
return $this->translateMessage($category, $message, $language);
} else {
return $message;
return false;
}
}
......@@ -96,7 +96,7 @@ class MessageSource extends Component
* @param string $category the category that the message belongs to
* @param string $message the message to be translated
* @param string $language the target language
* @return string the translated message
* @return string|boolean the translated message or false if translation wasn't found
*/
protected function translateMessage($category, $message, $language)
{
......@@ -113,9 +113,8 @@ class MessageSource extends Component
'language' => $language,
]);
$this->trigger(self::EVENT_MISSING_TRANSLATION, $event);
return $this->_messages[$key] = $event->message;
} else {
return $message;
$this->_messages[$key] = $event->message;
}
return false;
}
}
......@@ -151,7 +151,7 @@ class StringValidator extends Validator
$options = [
'message' => Yii::$app->getI18n()->format($this->message, [
'{attribute}' => $label,
'attribute' => $label,
], Yii::$app->language),
];
......
......@@ -98,7 +98,7 @@ class Request extends \yii\base\Request
* forms submitted via POST method must contain a hidden input whose name is specified by [[csrfVar]].
* You may use [[\yii\web\Html::beginForm()]] to generate his hidden input.
*
* In JavaScript, you may get the values of [[csrfVar]] and [[csrfToken]] via `yii.getCsrfVar()` and
* In JavaScript, you may get the values of [[csrfVar]] and [[csrfToken]] via `yii.getCsrfParam()` and
* `yii.getCsrfToken()`, respectively. The [[\yii\web\YiiAsset]] asset must be registered.
*
* @see Controller::enableCsrfValidation
......
......@@ -454,7 +454,7 @@ class View extends \yii\base\View
$request = Yii::$app->getRequest();
if ($request instanceof \yii\web\Request && $request->enableCsrfValidation && !$request->getIsAjax()) {
$lines[] = Html::tag('meta', '', ['name' => 'csrf-var', 'content' => $request->csrfVar]);
$lines[] = Html::tag('meta', '', ['name' => 'csrf-param', 'content' => $request->csrfVar]);
$lines[] = Html::tag('meta', '', ['name' => 'csrf-token', 'content' => $request->getCsrfToken()]);
}
......
......@@ -160,25 +160,31 @@ abstract class BaseListView extends Widget
$page = $pagination->getPage() + 1;
$pageCount = $pagination->pageCount;
if (($summaryContent = $this->summary) === null) {
$summaryContent = '<div class="summary">'
. Yii::t('yii', 'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.')
return '<div class="summary">'
. Yii::t('yii', 'Showing <b>{begin, number}-{end, number}</b> of <b>{totalCount, number}</b> {totalCount, plural, one{item} other{items}}.', [
'begin' => $begin,
'end' => $end,
'count' => $count,
'totalCount' => $totalCount,
'page' => $page,
'pageCount' => $pageCount,
])
. '</div>';
}
} else {
$begin = $page = $pageCount = 1;
$end = $totalCount = $count;
if (($summaryContent = $this->summary) === null) {
$summaryContent = '<div class="summary">' . Yii::t('yii', 'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.') . '</div>';
return '<div class="summary">' . Yii::t('yii', 'Total <b>{count, number}</b> {count, plural, one{item} other{items}}.', [
'begin' => $begin,
'end' => $end,
'count' => $count,
'totalCount' => $totalCount,
'page' => $page,
'pageCount' => $pageCount,
]) . '</div>';
}
}
return Yii::$app->getI18n()->format($summaryContent, [
'begin' => $begin,
'end' => $end,
'count' => $count,
'totalCount' => $totalCount,
'page' => $page,
'pageCount' => $pageCount,
], Yii::$app->language);
}
/**
......
......@@ -240,6 +240,55 @@ class CollectionTest extends MongoDbTestCase
$this->assertEquals($expectedRows, $rows);
}
/**
* @depends testMapReduce
*/
public function testMapReduceInline()
{
$collection = $this->getConnection()->getCollection('customer');
$rows = [
[
'name' => 'customer 1',
'status' => 1,
'amount' => 100,
],
[
'name' => 'customer 2',
'status' => 1,
'amount' => 200,
],
[
'name' => 'customer 2',
'status' => 2,
'amount' => 400,
],
[
'name' => 'customer 2',
'status' => 3,
'amount' => 500,
],
];
$collection->batchInsert($rows);
$result = $collection->mapReduce(
'function () {emit(this.status, this.amount)}',
'function (key, values) {return Array.sum(values)}',
['inline' => true],
['status' => ['$lt' => 3]]
);
$expectedRows = [
[
'_id' => 1,
'value' => 300,
],
[
'_id' => 2,
'value' => 400,
],
];
$this->assertEquals($expectedRows, $result);
}
public function testCreateIndex()
{
$collection = $this->getConnection()->getCollection('customer');
......
......@@ -303,6 +303,57 @@ class ComponentTest extends TestCase
$this->assertNull($component->getBehavior('a'));
$this->assertNull($component->getBehavior('b'));
}
public function testSetReadOnlyProperty()
{
$this->setExpectedException(
'\yii\base\InvalidCallException',
'Setting read-only property: yiiunit\framework\base\NewComponent::object'
);
$this->component->object = 'z';
}
public function testSetPropertyOfBehavior()
{
$this->assertNull($this->component->getBehavior('a'));
$behavior = new NewBehavior;
$this->component->attachBehaviors([
'a' => $behavior,
]);
$this->component->p = 'Yii is cool.';
$this->assertSame('Yii is cool.', $this->component->getBehavior('a')->p);
}
public function testSettingBehaviorWithSetter()
{
$behaviorName = 'foo';
$this->assertNull($this->component->getBehavior($behaviorName));
$p = 'as ' . $behaviorName;
$this->component->$p = __NAMESPACE__ . '\NewBehavior';
$this->assertSame(__NAMESPACE__ . '\NewBehavior', get_class($this->component->getBehavior($behaviorName)));
}
public function testWriteOnlyProperty()
{
$this->setExpectedException(
'\yii\base\InvalidCallException',
'Getting write-only property: yiiunit\framework\base\NewComponent::writeOnly'
);
$this->component->writeOnly;
}
public function testSuccessfulMethodCheck()
{
$this->assertTrue($this->component->hasMethod('hasProperty'));
}
public function testTurningOffNonExistingBehavior()
{
$this->assertFalse($this->component->hasEventHandlers('foo'));
$this->assertFalse($this->component->off('foo'));
}
}
class NewComponent extends Component
......@@ -357,6 +408,10 @@ class NewComponent extends Component
{
$this->trigger('click', new Event);
}
public function setWriteOnly()
{
}
}
class NewBehavior extends Behavior
......
......@@ -139,6 +139,21 @@ class ObjectTest extends TestCase
$object = new NewObject(['text' => 'test text']);
$this->assertEquals('test text', $object->getText());
}
public function testGetClassName()
{
$object = $this->object;
$this->assertSame(get_class($object), $object::className());
}
public function testReadingWriteOnlyProperty()
{
$this->setExpectedException(
'yii\base\InvalidCallException',
'Getting write-only property: yiiunit\framework\base\NewObject::writeOnly'
);
$this->object->writeOnly;
}
}
......@@ -179,4 +194,6 @@ class NewObject extends Object
{
return $this->_items;
}
public function setWriteOnly(){}
}
......@@ -89,6 +89,15 @@ class I18NTest extends TestCase
$model = new ParamModel();
$this->assertEquals('His name is peer and he is 5 years old.', $this->i18n->translate('test', $msg, $model, 'en-US'));
}
/**
* When translation is missing source language should be used for formatting.
* https://github.com/yiisoft/yii2/issues/2209
*/
public function testMissingTranslationFormatting()
{
$this->assertEquals('1 item', $this->i18n->translate('test', '{0, number} {0, plural, one{item} other{items}}', 1, 'hu'));
}
}
class ParamModel extends Model
......
......@@ -163,7 +163,7 @@ class BaseMailerTest extends TestCase
$filePath = $this->getTestFilePath();
$viewName = 'test_view';
$viewName = 'test_view2';
$viewFileName = $filePath . DIRECTORY_SEPARATOR . $viewName . '.php';
$viewFileContent = 'view file content';
file_put_contents($viewFileName, $viewFileContent);
......
......@@ -173,7 +173,9 @@ abstract class ManagerTestCase extends TestCase
$this->assertTrue($this->auth->executeBizRule(null, [], null));
$this->assertTrue($this->auth->executeBizRule('return 1 == true;', [], null));
$this->assertTrue($this->auth->executeBizRule('return $params[0] == $params[1];', [1, '1'], null));
$this->assertFalse($this->auth->executeBizRule('invalid;', [], null));
if (defined('HHVM_VERSION')) { // invalid code crashes on HHVM
$this->assertFalse($this->auth->executeBizRule('invalid;', [], null));
}
}
public function testCheckAccess()
......
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