diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index 2f06923..e0534e5 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -11,6 +11,7 @@ Yii Framework 2 Change Log
- Bug #15194: Fixed `yii\db\QueryBuilder::insert()` to preserve passed params when building a `INSERT INTO ... SELECT` query for MSSQL, PostgreSQL and SQLite (sergeymakinen)
- Bug #15229: Fixed `yii\console\widgets\Table` default value for `getScreenWidth()`, when `Console::getScreenSize()` can't determine screen size (webleaf)
- Bug #15234: Fixed `\yii\widgets\LinkPager` removed `tag` from `disabledListItemSubTagOptions` (SDKiller)
+- Enh #7988: Added `\yii\helpers\Console::errorSummary()` and `\yii\helpers\Json::errorSummary()` (developeruz)
- Bug #15249: Controllers in subdirectories were not visible in commands list (IceJOKER)
- Bug #15270: Resolved potential race conditions when writing generated php-files (kalessil)
- Bug #15301: Fixed `ArrayHelper::filter()` to work properly with `0` in values (hhniao)
diff --git a/framework/base/Model.php b/framework/base/Model.php
index af97a86..5e1ddc7 100644
--- a/framework/base/Model.php
+++ b/framework/base/Model.php
@@ -611,6 +611,25 @@ class Model extends Component implements StaticInstanceInterface, IteratorAggreg
}
/**
+ * Returns the errors for all attributes as a one-dimensional array.
+ * @param bool $showAllErrors boolean, if set to true every error message for each attribute will be shown otherwise
+ * only the first error message for each attribute will be shown.
+ * @return array errors for all attributes as a one-dimensional array. Empty array is returned if no error.
+ * @see getErrors()
+ * @see getFirstErrors()
+ * @since 2.0.14
+ */
+ public function getErrorSummary($showAllErrors)
+ {
+ $lines = [];
+ $errors = $showAllErrors ? $this->getErrors() : $this->getFirstErrors();
+ foreach ($errors as $es) {
+ $lines = array_merge((array)$es, $lines);
+ }
+ return $lines;
+ }
+
+ /**
* Adds a new error to the specified attribute.
* @param string $attribute attribute name
* @param string $error new error message
diff --git a/framework/helpers/BaseConsole.php b/framework/helpers/BaseConsole.php
index 28a63ba..8d90af7 100644
--- a/framework/helpers/BaseConsole.php
+++ b/framework/helpers/BaseConsole.php
@@ -8,6 +8,7 @@
namespace yii\helpers;
use yii\console\Markdown as ConsoleMarkdown;
+use yii\base\Model;
/**
* BaseConsole provides concrete implementation for [[Console]].
@@ -1051,4 +1052,45 @@ class BaseConsole
self::$_progressEtaLastDone = 0;
self::$_progressEtaLastUpdate = null;
}
+
+ /**
+ * Generates a summary of the validation errors.
+ * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.
+ * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
+ *
+ * - showAllErrors: boolean, if set to true every error message for each attribute will be shown otherwise
+ * only the first error message for each attribute will be shown. Defaults to `false`.
+ *
+ * @return string the generated error summary
+ * @since 2.0.14
+ */
+ public static function errorSummary($models, $options = [])
+ {
+ $showAllErrors = ArrayHelper::remove($options, 'showAllErrors', false);
+ $lines = self::collectErrors($models, $showAllErrors);
+
+ return implode(PHP_EOL, $lines);
+ }
+
+ /**
+ * Return array of the validation errors
+ * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.
+ * @param $showAllErrors boolean, if set to true every error message for each attribute will be shown otherwise
+ * only the first error message for each attribute will be shown.
+ * @return array of the validation errors
+ * @since 2.0.14
+ */
+ private static function collectErrors($models, $showAllErrors)
+ {
+ $lines = [];
+ if (!is_array($models)) {
+ $models = [$models];
+ }
+
+ foreach ($models as $model) {
+ $lines = array_unique(array_merge($lines, $model->getErrorSummary($showAllErrors)));
+ }
+
+ return $lines;
+ }
}
diff --git a/framework/helpers/BaseHtml.php b/framework/helpers/BaseHtml.php
index 4cea3d8..2ff7ca6 100644
--- a/framework/helpers/BaseHtml.php
+++ b/framework/helpers/BaseHtml.php
@@ -1216,35 +1216,45 @@ class BaseHtml
$encode = ArrayHelper::remove($options, 'encode', true);
$showAllErrors = ArrayHelper::remove($options, 'showAllErrors', false);
unset($options['header']);
+ $lines = self::collectErrors($models, $encode, $showAllErrors);
+ if (empty($lines)) {
+ // still render the placeholder for client-side validation use
+ $content = '
';
+ $options['style'] = isset($options['style']) ? rtrim($options['style'], ';') . '; display:none' : 'display:none';
+ } else {
+ $content = '- ' . implode("
\n- ", $lines) . '
';
+ }
+ return Html::tag('div', $header . $content . $footer, $options);
+ }
+
+ /**
+ * Return array of the validation errors
+ * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.
+ * @param $encode boolean, if set to false then the error messages won't be encoded.
+ * @param $showAllErrors boolean, if set to true every error message for each attribute will be shown otherwise
+ * only the first error message for each attribute will be shown.
+ * @return array of the validation errors
+ * @since 2.0.14
+ */
+ private static function collectErrors($models, $encode, $showAllErrors)
+ {
$lines = [];
if (!is_array($models)) {
$models = [$models];
}
+
foreach ($models as $model) {
- /* @var $model Model */
- foreach ($model->getErrors() as $errors) {
- foreach ($errors as $error) {
- $line = $encode ? Html::encode($error) : $error;
- if (!in_array($line, $lines, true)) {
- $lines[] = $line;
- }
- if (!$showAllErrors) {
- break;
- }
- }
- }
+ $lines = array_unique(array_merge($lines, $model->getErrorSummary($showAllErrors)));
}
- if (empty($lines)) {
- // still render the placeholder for client-side validation use
- $content = '';
- $options['style'] = isset($options['style']) ? rtrim($options['style'], ';') . '; display:none' : 'display:none';
- } else {
- $content = '- ' . implode("
\n- ", $lines) . '
';
+ if ($encode) {
+ for ($i = 0; $i < count($lines); $i++) {
+ $lines[$i] = Html::encode($lines[$i]);
+ }
}
- return Html::tag('div', $header . $content . $footer, $options);
+ return $lines;
}
/**
diff --git a/framework/helpers/BaseJson.php b/framework/helpers/BaseJson.php
index 5ac9394..32a0c2b 100644
--- a/framework/helpers/BaseJson.php
+++ b/framework/helpers/BaseJson.php
@@ -10,6 +10,7 @@ namespace yii\helpers;
use yii\base\Arrayable;
use yii\base\InvalidParamException;
use yii\web\JsExpression;
+use yii\base\Model;
/**
* BaseJson provides concrete implementation for [[Json]].
@@ -179,4 +180,45 @@ class BaseJson
return $data;
}
+
+ /**
+ * Generates a summary of the validation errors.
+ * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.
+ * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
+ *
+ * - showAllErrors: boolean, if set to true every error message for each attribute will be shown otherwise
+ * only the first error message for each attribute will be shown. Defaults to `false`.
+ *
+ * @return string the generated error summary
+ * @since 2.0.14
+ */
+ public static function errorSummary($models, $options = [])
+ {
+ $showAllErrors = ArrayHelper::remove($options, 'showAllErrors', false);
+ $lines = self::collectErrors($models, $showAllErrors);
+
+ return json_encode($lines);
+ }
+
+ /**
+ * Return array of the validation errors
+ * @param Model|Model[] $models the model(s) whose validation errors are to be displayed.
+ * @param $showAllErrors boolean, if set to true every error message for each attribute will be shown otherwise
+ * only the first error message for each attribute will be shown.
+ * @return array of the validation errors
+ * @since 2.0.14
+ */
+ private static function collectErrors($models, $showAllErrors)
+ {
+ $lines = [];
+ if (!is_array($models)) {
+ $models = [$models];
+ }
+
+ foreach ($models as $model) {
+ $lines = array_unique(array_merge($lines, $model->getErrorSummary($showAllErrors)));
+ }
+
+ return $lines;
+ }
}
diff --git a/tests/framework/base/ModelTest.php b/tests/framework/base/ModelTest.php
index 303fc48..ca7f1c8 100644
--- a/tests/framework/base/ModelTest.php
+++ b/tests/framework/base/ModelTest.php
@@ -310,6 +310,9 @@ class ModelTest extends TestCase
'lastName' => ['Another one!'],
], $speaker->getErrors());
+ $this->assertEquals(['Another one!', 'Something is wrong!', 'Totally wrong!'], $speaker->getErrorSummary(true));
+ $this->assertEquals(['Another one!', 'Something is wrong!'], $speaker->getErrorSummary(false));
+
$speaker->clearErrors('firstName');
$this->assertEquals([
'lastName' => ['Another one!'],
diff --git a/tests/framework/helpers/ConsoleTest.php b/tests/framework/helpers/ConsoleTest.php
index bd9767f..88cac6d 100644
--- a/tests/framework/helpers/ConsoleTest.php
+++ b/tests/framework/helpers/ConsoleTest.php
@@ -10,6 +10,7 @@ namespace yiiunit\framework\helpers;
use Yii;
use yii\helpers\Console;
use yiiunit\TestCase;
+use yii\base\DynamicModel;
/**
* @group helpers
@@ -204,6 +205,39 @@ class ConsoleTest extends TestCase
$this->assertEquals($html, Console::ansiToHtml($ansi));
}
+ public function testErrorSummary()
+ {
+ $model = new TestConsoleModel();
+ $model->name = 'not_an_integer';
+ $model->addError('name', 'Error message. Here are some chars: < >');
+ $model->addError('name', 'Error message. Here are even more chars: ""');
+ $model->validate(null, false);
+ $options = ['showAllErrors' => true];
+ $expectedHtml = "Error message. Here are some chars: < >\nError message. Here are even more chars: \"\"";
+ $this->assertEquals($expectedHtml, Console::errorSummary($model, $options));
+ }
+}
+
+/**
+ * @property string name
+ * @property array types
+ * @property string description
+ */
+class TestConsoleModel extends DynamicModel
+{
+ public function rules()
+ {
+ return [
+ ['name', 'required'],
+ ['name', 'string', 'max' => 100]
+ ];
+ }
+
+ public function init()
+ {
+ $this->defineAttribute('name');
+ }
+
/**
* @covers \yii\helpers\BaseConsole::input()
*/
diff --git a/tests/framework/helpers/JsonTest.php b/tests/framework/helpers/JsonTest.php
index 82fefaf..3952788 100644
--- a/tests/framework/helpers/JsonTest.php
+++ b/tests/framework/helpers/JsonTest.php
@@ -7,7 +7,7 @@
namespace yiiunit\framework\helpers;
-use yii\base\Model;
+use yii\base\DynamicModel;
use yii\helpers\BaseJson;
use yii\helpers\Json;
use yii\web\JsExpression;
@@ -208,9 +208,21 @@ class JsonTest extends TestCase
}
}
}
+
+ public function testErrorSummary()
+ {
+ $model = new JsonModel();
+ $model->name = 'not_an_integer';
+ $model->addError('name', 'Error message. Here are some chars: < >');
+ $model->addError('name', 'Error message. Here are even more chars: ""');
+ $model->validate(null, false);
+ $options = ['showAllErrors' => true];
+ $expectedHtml = '["Error message. Here are some chars: < >","Error message. Here are even more chars: \"\""]';
+ $this->assertEquals($expectedHtml, Json::errorSummary($model, $options));
+ }
}
-class JsonModel extends Model implements \JsonSerializable
+class JsonModel extends DynamicModel implements \JsonSerializable
{
public $data = ['json' => 'serializable'];
@@ -218,4 +230,17 @@ class JsonModel extends Model implements \JsonSerializable
{
return $this->data;
}
+
+ public function rules()
+ {
+ return [
+ ['name', 'required'],
+ ['name', 'string', 'max' => 100]
+ ];
+ }
+
+ public function init()
+ {
+ $this->defineAttribute('name');
+ }
}