Commit 8bb3e07c by Klimov Paul

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

parents 60312771 d1e0a545
......@@ -11,6 +11,9 @@ return [
'mail' => [
'class' => 'yii\swiftmailer\Mailer',
'viewPath' => '@common/mail',
// send all mails to a file by default. You have to set
// 'useFileTransport' to false and configure a transport
// for the mailer to send real emails.
'useFileTransport' => true,
],
],
......
......@@ -19,6 +19,9 @@ $config = [
],
'mail' => [
'class' => 'yii\swiftmailer\Mailer',
// send all mails to a file by default. You have to set
// 'useFileTransport' to false and configure a transport
// for the mailer to send real emails.
'useFileTransport' => true,
],
'log' => [
......
......@@ -35,8 +35,8 @@ Application Structure
* [Applications](structure-applications.md)
* [Application Components](structure-application-components.md)
* [Controllers](structure-controllers.md)
* [Views](structure-views.md)
* [Models](structure-models.md)
* [Views](structure-views.md)
* **TBD** [Filters](structure-filters.md)
* **TBD** [Widgets](structure-widgets.md)
* **TBD** [Modules](structure-modules.md)
......@@ -88,9 +88,9 @@ Getting Data from Users
-----------------------
* [Creating Forms](input-forms.md)
* [Input Validation](input-validation.md)
* **TBD** [Uploading Files](input-file-uploading.md)
* **TBD** [Inputs for Multiple Models](input-multiple-models.md)
* [Validating Input](input-validation.md)
* **TBD** [Uploading Files](input-file-upload.md)
* **TBD** [Getting Data for Multiple Models](input-multiple-models.md)
Displaying Data
......
......@@ -8,8 +8,56 @@ pass them to [models](structure-models.md), inject model results into [views](st
and finally generate outgoing responses.
Controllers are composed by *actions* which are the most basic units that end users can address and request for
execution. A controller can have one or multiple actions. For example, you can have a `post` controller which
contains a `view` action. End users can request this `view` action which may display a requested post.
execution. A controller can have one or multiple actions.
The following example shows a `post` controller with two actions: `view` and `create`:
```php
namespace app\controllers;
use Yii;
use app\models\Post;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
class PostController extends Controller
{
public function actionView($id)
{
$model = Post::findOne($id);
if ($model !== null) {
return $this->render('view', [
'model' => $model,
]);
} else {
throw new NotFoundHttpException;
}
}
public function actionCreate()
{
$model = new Post;
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
}
```
In the `view` action (defined by the `actionView()` method), the code first loads the [model](structure-models.md)
according to the requested model ID; If the model is loaded successfully, it will display it using
a [view](structure-views.md) named `view`. Otherwise, it will throw an exception.
In the `create` action (defined by the `actionCreate()` method), the code is similar. It first tries to populate
the [model](structure-models.md) using the request data and save the model. If both succeed it will redirect
the browser to the `view` action with the ID of the newly created model. Otherwise it will display
the `create` view through which users can provide the needed input.
## Routes <a name="routes"></a>
......@@ -386,64 +434,13 @@ to fulfill the request:
## Best Practices <a name="best-practices"></a>
In a well-designed application, controllers are often very thin with each action containing only a few lines of code.
The main role of these code is to invoke appropriate [models](structure-models.md) with the request data
and use [views](structure-views.md) to present the models.
The following code is a typical example showing how the `view` and `create` actions should be implemented
in a controller.
```php
namespace app\controllers;
use Yii;
use app\models\Post;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
class PostController extends Controller
{
public function actionView($id)
{
$model = Post::findOne($id);
if ($model !== null) {
return $this->render('view', [
'model' => $model,
]);
} else {
throw new NotFoundHttpException;
}
}
public function actionCreate()
{
$model = new Post;
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
}
```
In the `view` action, the code first loads the model according to the requested model ID; If the model
is loaded successfully, it will display it using the view named `view`. Otherwise, it will throw an exception.
In the `create` action, the code is similar. It first tries to populate the model using the request data
and save the model. If both succeed it will redirect the browser to the `view` action with the ID of
the newly created model. Otherwise it will display the `create` view through which users can provide the needed input.
If your controller is rather complicated, it usually indicates that you should refactor it and move some code
to other classes.
In summary, a controller
In summary, controllers
* may access the [request](runtime-requests.md) data;
* may send commands to [models](structure-models.md) and [views](structure-views.md);
* should return the [response](runtime-responses.md) data;
* should NOT process the request data;
* should NOT build the response data.
If your controller is rather complicated, it usually indicates that you should refactor it and move some code
to other classes.
* may call methods of [models](structure-models.md) and other service components with request data;
* may use [views](structure-views.md) to compose responses;
* should NOT process the request data - this should be done in [models](structure-models.md);
* should avoid embedding HTML or other presentational code - this is better done in [views](structure-views.md).
......@@ -4,11 +4,12 @@ Yii Framework 2 elasticsearch extension Change Log
2.0.0-rc under development
--------------------------
- Bug #3587: Fixed an issue with storing empty records (cebe)
- Enh #3520: Added `unlinkAll()`-method to active record to remove all records of a model relation (NmDimas, samdark, cebe)
- Enh #3527: Added `highlight` property to Query and ActiveRecord. (Borales)
- Chg: asArray in ActiveQuery is now equal to using the normal Query. This means, that the output structure has changed and `with` is supported anymore. (cebe)
- Chg: Deletion of a record is now also considered successful if the record did not exist. (cebe)
- Chg: Requirement changes: Yii now requires elasticsearch version 1.0 or higher (cebe)
- Enh #3520: Added `unlinkAll()`-method to active record to remove all records of a model relation (NmDimas, samdark, cebe)
- Enh #3527: Added `highlight` property to Query and ActiveRecord. (Borales)
2.0.0-beta April 13, 2014
......@@ -27,7 +28,9 @@ Yii Framework 2 elasticsearch extension Change Log
All relational queries are now directly served by `ActiveQuery` allowing to use
custom scopes in relations (cebe)
2.0.0-alpha, December 1, 2013
-----------------------------
- Initial release.
......@@ -80,7 +80,11 @@ class Command extends Component
*/
public function insert($index, $type, $data, $id = null, $options = [])
{
if (empty($data)) {
$body = '{}';
} else {
$body = is_array($data) ? Json::encode($data) : $data;
}
if ($id !== null) {
return $this->db->put([$index, $type, $id], $options, $body);
......
......@@ -6,6 +6,7 @@ Yii Framework 2 gii extension Change Log
- Bug #1263: Fixed the issue that Gii and Debug modules might be affected by incompatible asset manager configuration (qiangxue)
- Bug #3265: Fixed incorrect controller class name validation (suralc)
- Bug #3693: Fixed broken Gii preview when a file is unchanged (cebe)
- Enh #2018: Search model is not required anymore in CRUD generator (johonunu)
- Enh #3088: The gii module will manage their own URL rules now (qiangxue)
- Enh #3222: Added `useTablePrefix` option to the model generator for Gii (horizons2)
......
......@@ -45,7 +45,14 @@ yii.gii = (function ($) {
$modal.find('.modal-title').text($link.data('title'));
$modal.find('.modal-body').html('Loading ...');
$modal.modal('show');
var checked = $('a.' + $modal.data('action') + '[href="' + $link.attr('href') + '"]').closest('tr').find('input').get(0).checked;
var checkbox = $('a.' + $modal.data('action') + '[href="' + $link.attr('href') + '"]').closest('tr').find('input').get(0);
var checked = false;
if (checkbox) {
checked = checkbox.checked;
$modal.find('.modal-checkbox').removeClass('disabled');
} else {
$modal.find('.modal-checkbox').addClass('disabled');
}
$modal.find('.modal-checkbox span').toggleClass('glyphicon-check', checked).toggleClass('glyphicon-unchecked', !checked);
$.ajax({
type: 'POST',
......
......@@ -10,7 +10,7 @@ namespace yii\base;
use yii\validators\Validator;
/**
* DynamicModel is a model class primarily used to support ad-hoc data validation.
* DynamicModel is a model class primarily used to support ad hoc data validation.
*
* The typical usage of DynamicModel is as follows,
*
......
......@@ -12,7 +12,7 @@ use yii\base\InvalidConfigException;
use yii\helpers\Html;
/**
* CompareValidator compares the specified attribute value with another value and validates if they are equal.
* CompareValidator compares the specified attribute value with another value.
*
* The value being compared with can be another attribute value
* (specified via [[compareAttribute]]) or a constant (specified via
......@@ -47,14 +47,14 @@ class CompareValidator extends Validator
/**
* @var string the operator for comparison. The following operators are supported:
*
* - '==': validates to see if the two values are equal. The comparison is done is non-strict mode.
* - '===': validates to see if the two values are equal. The comparison is done is strict mode.
* - '!=': validates to see if the two values are NOT equal. The comparison is done is non-strict mode.
* - '!==': validates to see if the two values are NOT equal. The comparison is done is strict mode.
* - `>`: validates to see if the value being validated is greater than the value being compared with.
* - `>=`: validates to see if the value being validated is greater than or equal to the value being compared with.
* - `<`: validates to see if the value being validated is less than the value being compared with.
* - `<=`: validates to see if the value being validated is less than or equal to the value being compared with.
* - `==`: check if two values are equal. The comparison is done is non-strict mode.
* - `===`: check if two values are equal. The comparison is done is strict mode.
* - `!=`: check if two values are NOT equal. The comparison is done is non-strict mode.
* - `!==`: check if two values are NOT equal. The comparison is done is strict mode.
* - `>`: check if value being validated is greater than the value being compared with.
* - `>=`: check if value being validated is greater than or equal to the value being compared with.
* - `<`: check if value being validated is less than the value being compared with.
* - `<=`: check if value being validated is less than or equal to the value being compared with.
*/
public $operator = '==';
/**
......
......@@ -32,6 +32,11 @@ class Order extends ActiveRecord
return $this->hasMany(OrderItem::className(), ['order_id' => 'id']);
}
public function getOrderItemsWithNullFK()
{
return $this->hasMany(OrderItemWithNullFK::className(), ['order_id' => 'id']);
}
public function getItems()
{
return $this->hasMany(Item::className(), ['id' => 'item_id'])
......@@ -72,7 +77,7 @@ class Order extends ActiveRecord
public function getBooksWithNullFK()
{
return $this->hasMany(Item::className(), ['id' => 'item_id'])
->via('orderItemsWithNullFk')
->via('orderItemsWithNullFK')
->where(['category_id' => 1]);
}
......
......@@ -186,6 +186,16 @@ class ActiveRecordTest extends ElasticSearchTestCase
$db->createCommand()->flushIndex('yiitest');
}
public function testSaveNoChanges()
{
// this should not fail with exception
$customer = new Customer();
// insert
$customer->save(false);
// update
$customer->save(false);
}
public function testFindAsArray()
{
// asArray
......
......@@ -773,23 +773,23 @@ trait ActiveRecordTestTrait
/** @var Order $order */
$order = $orderClass::findOne(1);
$this->assertEquals(2, count($order->books));
$this->assertEquals(6, $orderItemClass::find()->count());
$orderItemCount = $orderItemClass::find()->count();
$this->assertEquals(5, $itemClass::find()->count());
$order->unlinkAll('books', true);
$this->afterSave();
$this->assertEquals(5, $itemClass::find()->count());
$this->assertEquals(4, $orderItemClass::find()->count());
$this->assertEquals($orderItemCount - 2, $orderItemClass::find()->count());
$this->assertEquals(0, count($order->books));
// via model without delete
$this->assertEquals(2, count($order->booksWithNullFK));
$this->assertEquals(6, $orderItemsWithNullFKClass::find()->count());
$orderItemCount = $orderItemsWithNullFKClass::find()->count();
$this->assertEquals(5, $itemClass::find()->count());
$order->unlinkAll('booksWithNullFK',false);
$this->afterSave();
$this->assertEquals(0, count($order->booksWithNullFK));
$this->assertEquals(2,$orderItemsWithNullFKClass::find()->where(['AND', ['item_id' => [1, 2]], ['order_id' => null]])->count());
$this->assertEquals(6, $orderItemsWithNullFKClass::find()->count());
$this->assertEquals(2, $orderItemsWithNullFKClass::find()->where(['AND', ['item_id' => [1, 2]], ['order_id' => null]])->count());
$this->assertEquals($orderItemCount, $orderItemsWithNullFKClass::find()->count());
$this->assertEquals(5, $itemClass::find()->count());
// via table is covered in \yiiunit\framework\db\ActiveRecordTest::testUnlinkAllViaTable()
......
......@@ -543,30 +543,30 @@ class ActiveRecordTest extends DatabaseTestCase
/** @var \yii\db\ActiveRecordInterface $orderItemClass */
$orderItemClass = $this->getOrderItemClass();
/** @var \yii\db\ActiveRecordInterface $itemClass */
$itemClass = $this->getOrderItemClass();
$itemClass = $this->getItemClass();
/** @var \yii\db\ActiveRecordInterface $orderItemsWithNullFKClass */
$orderItemsWithNullFKClass = $this->getOrderItemWithNullFKmClass();
// via table with delete
/** @var Order $order */
$order = $orderClass::findOne(1);
$this->assertEquals(2, count($order->books));
$this->assertEquals(6, $orderItemClass::find()->count());
$this->assertEquals(2, count($order->booksViaTable));
$orderItemCount = $orderItemClass::find()->count();
$this->assertEquals(5, $itemClass::find()->count());
$order->unlinkAll('booksViaTable', true);
$this->afterSave();
$this->assertEquals(5, $itemClass::find()->count());
$this->assertEquals(4, $orderItemClass::find()->count());
$this->assertEquals(0, count($order->books));
$this->assertEquals($orderItemCount - 2, $orderItemClass::find()->count());
$this->assertEquals(0, count($order->booksViaTable));
// via table without delete
$this->assertEquals(2, count($order->booksWithNullFK));
$this->assertEquals(6, $orderItemsWithNullFKClass::find()->count());
$this->assertEquals(2, count($order->booksWithNullFKViaTable));
$orderItemCount = $orderItemsWithNullFKClass::find()->count();
$this->assertEquals(5, $itemClass::find()->count());
$order->unlinkAll('booksWithNullFKViaTable',false);
$this->assertEquals(0, count($order->booksWithNullFK));
$this->assertEquals(0, count($order->booksWithNullFKViaTable));
$this->assertEquals(2,$orderItemsWithNullFKClass::find()->where(['AND', ['item_id' => [1, 2]], ['order_id' => null]])->count());
$this->assertEquals(6, $orderItemsWithNullFKClass::find()->count());
$this->assertEquals($orderItemCount, $orderItemsWithNullFKClass::find()->count());
$this->assertEquals(5, $itemClass::find()->count());
}
}
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