Commit 96e794b4 by Paul Klimov

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

parents dfefd060 3db7a075
...@@ -70,7 +70,7 @@ at [getcomposer.org](http://getcomposer.org/doc/00-intro.md#installation-nix). ...@@ -70,7 +70,7 @@ at [getcomposer.org](http://getcomposer.org/doc/00-intro.md#installation-nix).
You can then install the application using the following command: You can then install the application using the following command:
~~~ ~~~
php composer.phar create-project --stability=alpha yiisoft/yii2-app-advanced advanced php composer.phar create-project --stability=dev yiisoft/yii2-app-advanced advanced
~~~ ~~~
......
...@@ -52,7 +52,7 @@ at [getcomposer.org](http://getcomposer.org/doc/00-intro.md#installation-nix). ...@@ -52,7 +52,7 @@ at [getcomposer.org](http://getcomposer.org/doc/00-intro.md#installation-nix).
You can then install this application template using the following command: You can then install this application template using the following command:
~~~ ~~~
php composer.phar create-project --stability=alpha yiisoft/yii2-app-basic basic php composer.phar create-project --stability=dev yiisoft/yii2-app-basic basic
~~~ ~~~
Now you should be able to access the application through the following URL, assuming `basic` is the directory Now you should be able to access the application through the following URL, assuming `basic` is the directory
......
...@@ -48,6 +48,13 @@ $requirements = [ ...@@ -48,6 +48,13 @@ $requirements = [
'by' => 'All <a href="http://www.yiiframework.com/doc/api/#system.db">DB-related classes</a>', 'by' => 'All <a href="http://www.yiiframework.com/doc/api/#system.db">DB-related classes</a>',
'memo' => 'Required for MySQL database.', 'memo' => 'Required for MySQL database.',
], ],
[
'name' => 'PDO PostgreSQL extension',
'mandatory' => false,
'condition' => extension_loaded('pdo_pgsql'),
'by' => 'All <a href="http://www.yiiframework.com/doc/api/#system.db">DB-related classes</a>',
'memo' => 'Required for PostgreSQL database.',
],
// Cache : // Cache :
[ [
'name' => 'Memcache extension', 'name' => 'Memcache extension',
......
...@@ -125,7 +125,7 @@ class BlogController extends Controller ...@@ -125,7 +125,7 @@ class BlogController extends Controller
throw new NotFoundHttpException; throw new NotFoundHttpException;
} }
if (\Yii::$app->request->isPost)) { if (\Yii::$app->request->isPost) {
$post->load($_POST); $post->load($_POST);
if ($post->save()) { if ($post->save()) {
$this->redirect(['view', 'id' => $post->id]); $this->redirect(['view', 'id' => $post->id]);
......
...@@ -2,8 +2,8 @@ URL Management ...@@ -2,8 +2,8 @@ URL Management
============== ==============
The concept of URL management in Yii fairly simple. URL management is based on the premise that the application uses internal routes and parameters The concept of URL management in Yii fairly simple. URL management is based on the premise that the application uses internal routes and parameters
everywhere. The framework itself will then translates routes into URLs, and translate URLs into routs, according to the URL manager's configuration. everywhere. The framework itself will then translates routes into URLs, and translate URLs into routes, according to the URL manager's configuration.
This approach allows you to change site-wide URLs merely by edited a single config file, without ever touching the application code. This approach allows you to change site-wide URLs merely by editing a single config file, without ever touching the application code.
Internal route Internal route
-------------- --------------
......
...@@ -61,7 +61,8 @@ class Module extends \yii\base\Module ...@@ -61,7 +61,8 @@ class Module extends \yii\base\Module
Yii::$app->getView()->on(View::EVENT_END_BODY, [$this, 'renderToolbar']); Yii::$app->getView()->on(View::EVENT_END_BODY, [$this, 'renderToolbar']);
}); });
foreach (array_merge($this->corePanels(), $this->panels) as $id => $config) { $this->panels = array_merge($this->corePanels(), $this->panels);
foreach ($this->panels as $id => $config) {
$config['module'] = $this; $config['module'] = $this;
$config['id'] = $id; $config['id'] = $id;
$this->panels[$id] = Yii::createObject($config); $this->panels[$id] = Yii::createObject($config);
......
...@@ -31,6 +31,12 @@ class Panel extends Component ...@@ -31,6 +31,12 @@ class Panel extends Component
*/ */
public $module; public $module;
public $data; public $data;
/**
* @var array array of actions to add to the debug modules default controller.
* This array will be merged with all other panels actions property.
* See [[yii\base\Controller::actions()]] for the format.
*/
public $actions = [];
/** /**
* @return string name of the panel * @return string name of the panel
......
...@@ -27,6 +27,15 @@ class DefaultController extends Controller ...@@ -27,6 +27,15 @@ class DefaultController extends Controller
*/ */
public $summary; public $summary;
public function actions()
{
$actions = [];
foreach($this->module->panels as $panel) {
$actions = array_merge($actions, $panel->actions);
}
return $actions;
}
public function actionIndex() public function actionIndex()
{ {
return $this->render('index', ['manifest' => $this->getManifest()]); return $this->render('index', ['manifest' => $this->getManifest()]);
...@@ -82,7 +91,7 @@ class DefaultController extends Controller ...@@ -82,7 +91,7 @@ class DefaultController extends Controller
return $this->_manifest; return $this->_manifest;
} }
protected function loadData($tag) public function loadData($tag)
{ {
$manifest = $this->getManifest(); $manifest = $this->getManifest();
if (isset($manifest[$tag])) { if (isset($manifest[$tag])) {
......
...@@ -94,7 +94,7 @@ class Command extends Component ...@@ -94,7 +94,7 @@ class Command extends Component
*/ */
public function get($index, $type, $id, $options = []) public function get($index, $type, $id, $options = [])
{ {
return $this->db->get([$index, $type, $id], $options, null, [200, 404]); return $this->db->get([$index, $type, $id], $options, null);
} }
/** /**
......
...@@ -177,10 +177,10 @@ class Connection extends Component ...@@ -177,10 +177,10 @@ class Connection extends Component
return new QueryBuilder($this); return new QueryBuilder($this);
} }
public function get($url, $options = [], $body = null) public function get($url, $options = [], $body = null, $raw = false)
{ {
$this->open(); $this->open();
return $this->httpRequest('GET', $this->createUrl($url, $options), $body); return $this->httpRequest('GET', $this->createUrl($url, $options), $body, $raw);
} }
public function head($url, $options = [], $body = null) public function head($url, $options = [], $body = null)
...@@ -189,37 +189,43 @@ class Connection extends Component ...@@ -189,37 +189,43 @@ class Connection extends Component
return $this->httpRequest('HEAD', $this->createUrl($url, $options), $body); return $this->httpRequest('HEAD', $this->createUrl($url, $options), $body);
} }
public function post($url, $options = [], $body = null) public function post($url, $options = [], $body = null, $raw = false)
{ {
$this->open(); $this->open();
return $this->httpRequest('POST', $this->createUrl($url, $options), $body); return $this->httpRequest('POST', $this->createUrl($url, $options), $body, $raw);
} }
public function put($url, $options = [], $body = null) public function put($url, $options = [], $body = null, $raw = false)
{ {
$this->open(); $this->open();
return $this->httpRequest('PUT', $this->createUrl($url, $options), $body); return $this->httpRequest('PUT', $this->createUrl($url, $options), $body, $raw);
} }
public function delete($url, $options = [], $body = null) public function delete($url, $options = [], $body = null, $raw = false)
{ {
$this->open(); $this->open();
return $this->httpRequest('DELETE', $this->createUrl($url, $options), $body); return $this->httpRequest('DELETE', $this->createUrl($url, $options), $body, $raw);
} }
private function createUrl($path, $options = []) private function createUrl($path, $options = [])
{ {
$url = implode('/', array_map(function($a) { if (!is_string($path)) {
return urlencode(is_array($a) ? implode(',', $a) : $a); $url = implode('/', array_map(function($a) {
}, $path)); return urlencode(is_array($a) ? implode(',', $a) : $a);
}, $path));
if (!empty($options)) { if (!empty($options)) {
$url .= '?' . http_build_query($options); $url .= '?' . http_build_query($options);
}
} else {
$url = $path;
if (!empty($options)) {
$url .= (strpos($url, '?') === false ? '?' : '&') . http_build_query($options);
}
} }
return [$this->nodes[$this->activeNode]['http_address'], $url]; return [$this->nodes[$this->activeNode]['http_address'], $url];
} }
protected function httpRequest($method, $url, $requestBody = null) protected function httpRequest($method, $url, $requestBody = null, $raw = false)
{ {
$method = strtoupper($method); $method = strtoupper($method);
...@@ -228,7 +234,7 @@ class Connection extends Component ...@@ -228,7 +234,7 @@ class Connection extends Component
$body = ''; $body = '';
$options = [ $options = [
CURLOPT_USERAGENT => 'Yii2 Framework ' . __CLASS__, CURLOPT_USERAGENT => 'Yii Framework 2 ' . __CLASS__,
CURLOPT_RETURNTRANSFER => false, CURLOPT_RETURNTRANSFER => false,
CURLOPT_HEADER => false, CURLOPT_HEADER => false,
// http://www.php.net/manual/en/function.curl-setopt.php#82418 // http://www.php.net/manual/en/function.curl-setopt.php#82418
...@@ -264,8 +270,11 @@ class Connection extends Component ...@@ -264,8 +270,11 @@ class Connection extends Component
if (is_array($url)) { if (is_array($url)) {
list($host, $q) = $url; list($host, $q) = $url;
if (strncmp($host, 'inet[/', 6) == 0) { if (strncmp($host, 'inet[', 5) == 0) {
$host = substr($host, 6, -1); $host = substr($host, 5, -1);
if (($pos = strpos($host, '/')) !== false) {
$host = substr($host, $pos + 1);
}
} }
$profile = $method . ' ' . $q . '#' . $requestBody; $profile = $method . ' ' . $q . '#' . $requestBody;
$url = 'http://' . $host . '/' . $q; $url = 'http://' . $host . '/' . $q;
...@@ -312,7 +321,7 @@ class Connection extends Component ...@@ -312,7 +321,7 @@ class Connection extends Component
]); ]);
} }
if (isset($headers['content-type']) && !strncmp($headers['content-type'], 'application/json', 16)) { if (isset($headers['content-type']) && !strncmp($headers['content-type'], 'application/json', 16)) {
return Json::decode($body); return $raw ? $body : Json::decode($body);
} }
throw new Exception('Unsupported data received from elasticsearch: ' . $headers['content-type'], [ throw new Exception('Unsupported data received from elasticsearch: ' . $headers['content-type'], [
'requestMethod' => $method, 'requestMethod' => $method,
......
<?php
/**
* @author Carsten Brandt <mail@cebe.cc>
*/
namespace yii\elasticsearch;
use yii\base\Action;
use yii\base\NotSupportedException;
use yii\debug\Panel;
use yii\helpers\ArrayHelper;
use yii\web\HttpException;
use Yii;
use yii\web\Response;
class DebugAction extends Action
{
/**
* @var string the connection id to use
*/
public $db;
/**
* @var Panel
*/
public $panel;
public function run($logId, $tag)
{
$this->controller->loadData($tag);
$timings = $this->panel->calculateTimings();
ArrayHelper::multisort($timings, 3, SORT_DESC);
if (!isset($timings[$logId])) {
throw new HttpException(404, 'Log message not found.');
}
$message = $timings[$logId][1];
if (($pos = mb_strpos($message, "#")) !== false) {
$url = mb_substr($message, 0, $pos);
$body = mb_substr($message, $pos + 1);
} else {
$url = $message;
$body = null;
}
$method = mb_substr($url, 0, $pos = mb_strpos($url, ' '));
$url = mb_substr($url, $pos + 1);
$options = ['pretty' => true];
/** @var Connection $db */
$db = \Yii::$app->getComponent($this->db);
$time = microtime(true);
switch($method) {
case 'GET': $result = $db->get($url, $options, $body, true); break;
case 'POST': $result = $db->post($url, $options, $body, true); break;
case 'PUT': $result = $db->put($url, $options, $body, true); break;
case 'DELETE': $result = $db->delete($url, $options, $body, true); break;
case 'HEAD': $result = $db->head($url, $options, $body); break;
default:
throw new NotSupportedException("Request method '$method' is not supported by elasticsearch.");
}
$time = microtime(true) - $time;
if ($result === true) {
$result = '<span class="label label-success">success</span>';
} elseif ($result === false) {
$result = '<span class="label label-danger">no success</span>';
}
Yii::$app->response->format = Response::FORMAT_JSON;
return [
'time' => sprintf('%.1f ms', $time * 1000),
'result' => $result,
];
}
}
\ No newline at end of file
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
namespace yii\elasticsearch; namespace yii\elasticsearch;
use yii\debug\Panel; use yii\debug\Panel;
use yii\helpers\ArrayHelper;
use yii\log\Logger; use yii\log\Logger;
use yii\helpers\Html; use yii\helpers\Html;
use yii\web\View; use yii\web\View;
...@@ -20,6 +21,17 @@ use yii\web\View; ...@@ -20,6 +21,17 @@ use yii\web\View;
*/ */
class DebugPanel extends Panel class DebugPanel extends Panel
{ {
public $db = 'elasticsearch';
public function init()
{
$this->actions['elasticsearch-query'] = [
'class' => 'yii\\elasticsearch\\DebugAction',
'panel' => $this,
'db' => $this->db,
];
}
public function getName() public function getName()
{ {
return 'Elasticsearch'; return 'Elasticsearch';
...@@ -47,13 +59,14 @@ EOD; ...@@ -47,13 +59,14 @@ EOD;
public function getDetail() public function getDetail()
{ {
$timings = $this->calculateTimings();
ArrayHelper::multisort($timings, 3, SORT_DESC);
$rows = []; $rows = [];
$i = 0; $i = 0;
foreach ($this->data['messages'] as $log) { foreach ($timings as $logId => $timing) {
list ($message, $level, $category, $time, $traces) = $log; $duration = sprintf('%.1f ms', $timing[3] * 1000);
if ($level == Logger::LEVEL_PROFILE_BEGIN) { $message = $timing[1];
continue; $traces = $timing[4];
}
if (($pos = mb_strpos($message, "#")) !== false) { if (($pos = mb_strpos($message, "#")) !== false) {
$url = mb_substr($message, 0, $pos); $url = mb_substr($message, 0, $pos);
$body = mb_substr($message, $pos + 1); $body = mb_substr($message, $pos + 1);
...@@ -66,48 +79,43 @@ EOD; ...@@ -66,48 +79,43 @@ EOD;
$traceString .= Html::ul($traces, [ $traceString .= Html::ul($traces, [
'class' => 'trace', 'class' => 'trace',
'item' => function ($trace) { 'item' => function ($trace) {
return "<li>{$trace['file']}({$trace['line']})</li>"; return "<li>{$trace['file']}({$trace['line']})</li>";
}, },
]); ]);
} }
$runLinks = ''; $ajaxUrl = Html::url(['elasticsearch-query', 'logId' => $logId, 'tag' => $this->tag]);
$c = 0; \Yii::$app->view->registerJs(<<<JS
\Yii::$app->elasticsearch->open(); $('#elastic-link-$i').on('click', function() {
foreach(\Yii::$app->elasticsearch->nodes as $node) { var result = $('#elastic-result-$i');
$pos = mb_strpos($url, ' '); result.html('Sending request...');
$type = mb_substr($url, 0, $pos); result.parent('tr').show();
if ($type == 'GET' && !empty($body)) {
$type = 'POST';
}
$host = $node['http_address'];
if (strncmp($host, 'inet[/', 6) == 0) {
$host = substr($host, 6, -1);
}
$nodeUrl = 'http://' . $host . '/' . mb_substr($url, $pos + 1);
$nodeUrl .= (strpos($nodeUrl, '?') === false) ? '?pretty=true' : '&pretty=true';
$nodeBody = json_encode($body);
\Yii::$app->view->registerJs(<<<JS
$('#elastic-link-$i-$c').on('click', function() {
$('#elastic-result-$i').html('Sending $type request to $nodeUrl...');
$('#elastic-result-$i').parent('tr').show();
$.ajax({ $.ajax({
type: "$type", type: "POST",
url: "$nodeUrl", url: "$ajaxUrl",
body: $nodeBody,
success: function( data ) { success: function( data ) {
$('#elastic-result-$i').html(data); $('#elastic-time-$i').html(data.time);
$('#elastic-result-$i').html(data.result);
}, },
dataType: "text" error: function(jqXHR, textStatus, errorThrown) {
$('#elastic-time-$i').html('');
$('#elastic-result-$i').html('<span style="color: #c00;">Error: ' + errorThrown + ' - ' + textStatus + '</span><br />' + jqXHR.responseText);
},
dataType: "json"
}); });
return false; return false;
}); });
JS JS
, View::POS_READY); , View::POS_READY);
$runLinks .= Html::a(isset($node['name']) ? $node['name'] : $node['http_address'], '#', ['id' => "elastic-link-$i-$c"]) . '<br/>'; $runLink = Html::a('run query', '#', ['id' => "elastic-link-$i"]) . '<br/>';
$c++; $rows[] = <<<HTML
} <tr>
$rows[] = "<tr><td style=\"width: 80%;\"><div><b>$url</b><br/><p>$body</p>$traceString</div></td><td style=\"width: 20%;\">$runLinks</td></tr><tr style=\"display: none;\"><td colspan=\"2\" id=\"elastic-result-$i\"></td></tr>"; <td style="width: 10%;">$duration</td>
<td style="width: 75%;"><div><b>$url</b><br/><p>$body</p>$traceString</div></td>
<td style="width: 15%;">$runLink</td>
</tr>
<tr style="display: none;"><td id="elastic-time-$i"></td><td colspan="3" id="elastic-result-$i"></td></tr>
HTML;
$i++; $i++;
} }
$rows = implode("\n", $rows); $rows = implode("\n", $rows);
...@@ -117,8 +125,9 @@ JS ...@@ -117,8 +125,9 @@ JS
<table class="table table-condensed table-bordered table-striped table-hover" style="table-layout: fixed;"> <table class="table table-condensed table-bordered table-striped table-hover" style="table-layout: fixed;">
<thead> <thead>
<tr> <tr>
<th style="width: 80%;">Url / Query</th> <th style="width: 10%;">Time</th>
<th style="width: 20%;">Run Query on node</th> <th style="width: 75%;">Url / Query</th>
<th style="width: 15%;">Run Query on node</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
...@@ -130,7 +139,7 @@ HTML; ...@@ -130,7 +139,7 @@ HTML;
private $_timings; private $_timings;
protected function calculateTimings() public function calculateTimings()
{ {
if ($this->_timings !== null) { if ($this->_timings !== null) {
return $this->_timings; return $this->_timings;
......
...@@ -172,3 +172,5 @@ Add the following to you application config to enable it: ...@@ -172,3 +172,5 @@ Add the following to you application config to enable it:
], ],
// ... // ...
``` ```
![elasticsearch DebugPanel](README-debug.png)
\ No newline at end of file
...@@ -348,7 +348,6 @@ class Generator extends \yii\gii\Generator ...@@ -348,7 +348,6 @@ class Generator extends \yii\gii\Generator
case Schema::TYPE_TIME: case Schema::TYPE_TIME:
case Schema::TYPE_DATETIME: case Schema::TYPE_DATETIME:
case Schema::TYPE_TIMESTAMP: case Schema::TYPE_TIMESTAMP:
case
$conditions[] = "\$this->addCondition(\$query, '{$column}');"; $conditions[] = "\$this->addCondition(\$query, '{$column}');";
break; break;
default: default:
......
...@@ -397,10 +397,10 @@ class Generator extends \yii\gii\Generator ...@@ -397,10 +397,10 @@ class Generator extends \yii\gii\Generator
} }
$name = $rawName = Inflector::id2camel($key, '_'); $name = $rawName = Inflector::id2camel($key, '_');
$i = 0; $i = 0;
while (isset($table->columns[$name])) { while (isset($table->columns[lcfirst($name)])) {
$name = $rawName . ($i++); $name = $rawName . ($i++);
} }
while (isset($relations[$className][$name])) { while (isset($relations[$className][lcfirst($name)])) {
$name = $rawName . ($i++); $name = $rawName . ($i++);
} }
......
...@@ -128,7 +128,6 @@ ...@@ -128,7 +128,6 @@
data = $form.data('yiiActiveForm'); data = $form.data('yiiActiveForm');
if (data.validated) { if (data.validated) {
// continue submitting the form since validation passes // continue submitting the form since validation passes
data.validated = false;
return true; return true;
} }
......
...@@ -23,10 +23,13 @@ use yii\base\Component; ...@@ -23,10 +23,13 @@ use yii\base\Component;
* *
* ~~~ * ~~~
* $query = new Query; * $query = new Query;
* // compose the query
* $query->select('id, name') * $query->select('id, name')
* ->from('tbl_user') * ->from('tbl_user')
* ->limit(10); * ->limit(10);
* // build and execute the query * // build and execute the query
* $rows = $query->all();
* // alternatively, you can create DB command and execute it
* $command = $query->createCommand(); * $command = $query->createCommand();
* // $command->sql returns the actual SQL * // $command->sql returns the actual SQL
* $rows = $command->queryAll(); * $rows = $command->queryAll();
......
...@@ -327,6 +327,29 @@ class BaseArrayHelper ...@@ -327,6 +327,29 @@ class BaseArrayHelper
} }
/** /**
* Checks if the given array contains the specified key.
* This method enhances the `array_key_exists()` function by supporting case-insensitive
* key comparison.
* @param string $key the key to check
* @param array $array the array with keys to check
* @param boolean $caseSensitive whether the key comparison should be case-sensitive
* @return boolean whether the array contains the specified key
*/
public static function keyExists($key, $array, $caseSensitive = true)
{
if ($caseSensitive) {
return array_key_exists($key, $array);
} else {
foreach (array_keys($array) as $k) {
if (strcasecmp($key, $k) === 0) {
return true;
}
}
return false;
}
}
/**
* Sorts an array of objects or arrays (with the same structure) by one or several keys. * Sorts an array of objects or arrays (with the same structure) by one or several keys.
* @param array $array the array to be sorted. The array will be modified after calling this method. * @param array $array the array to be sorted. The array will be modified after calling this method.
* @param string|\Closure|array $key the key(s) to be sorted by. This refers to a key name of the sub-array * @param string|\Closure|array $key the key(s) to be sorted by. This refers to a key name of the sub-array
......
...@@ -551,8 +551,11 @@ class BaseHtml ...@@ -551,8 +551,11 @@ class BaseHtml
* in HTML code such as an image tag. If this is is coming from end users, you should [[encode()]] it to prevent XSS attacks. * in HTML code such as an image tag. If this is is coming from end users, you should [[encode()]] it to prevent XSS attacks.
* When this option is specified, the radio button will be enclosed by a label tag. * When this option is specified, the radio button will be enclosed by a label tag.
* - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified. * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified.
* - container: array|boolean, the HTML attributes for the container tag. This is only used when the "label" option is specified.
* If it is false, no container will be rendered. If it is an array or not, a "div" container will be rendered
* around the the radio button.
* *
* The rest of the options will be rendered as the attributes of the resulting tag. The values will * The rest of the options will be rendered as the attributes of the resulting radio button tag. The values will
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
* *
* @return string the generated radio button tag * @return string the generated radio button tag
...@@ -571,9 +574,14 @@ class BaseHtml ...@@ -571,9 +574,14 @@ class BaseHtml
if (isset($options['label'])) { if (isset($options['label'])) {
$label = $options['label']; $label = $options['label'];
$labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : []; $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : [];
unset($options['label'], $options['labelOptions']); $container = isset($options['container']) ? $options['container'] : ['class' => 'radio'];
unset($options['label'], $options['labelOptions'], $options['container']);
$content = static::label(static::input('radio', $name, $value, $options) . ' ' . $label, null, $labelOptions); $content = static::label(static::input('radio', $name, $value, $options) . ' ' . $label, null, $labelOptions);
return $hidden . static::tag('div', $content, ['class' => 'radio']); if (is_array($container)) {
return $hidden . static::tag('div', $content, $container);
} else {
return $hidden . $content;
}
} else { } else {
return $hidden . static::input('radio', $name, $value, $options); return $hidden . static::input('radio', $name, $value, $options);
} }
...@@ -592,8 +600,11 @@ class BaseHtml ...@@ -592,8 +600,11 @@ class BaseHtml
* in HTML code such as an image tag. If this is is coming from end users, you should [[encode()]] it to prevent XSS attacks. * in HTML code such as an image tag. If this is is coming from end users, you should [[encode()]] it to prevent XSS attacks.
* When this option is specified, the checkbox will be enclosed by a label tag. * When this option is specified, the checkbox will be enclosed by a label tag.
* - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified. * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified.
* - container: array|boolean, the HTML attributes for the container tag. This is only used when the "label" option is specified.
* If it is false, no container will be rendered. If it is an array or not, a "div" container will be rendered
* around the the radio button.
* *
* The rest of the options will be rendered as the attributes of the resulting tag. The values will * The rest of the options will be rendered as the attributes of the resulting checkbox tag. The values will
* be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered. * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
* *
* @return string the generated checkbox tag * @return string the generated checkbox tag
...@@ -612,9 +623,14 @@ class BaseHtml ...@@ -612,9 +623,14 @@ class BaseHtml
if (isset($options['label'])) { if (isset($options['label'])) {
$label = $options['label']; $label = $options['label'];
$labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : []; $labelOptions = isset($options['labelOptions']) ? $options['labelOptions'] : [];
unset($options['label'], $options['labelOptions']); $container = isset($options['container']) ? $options['container'] : ['class' => 'checkbox'];
unset($options['label'], $options['labelOptions'], $options['container']);
$content = static::label(static::input('checkbox', $name, $value, $options) . ' ' . $label, null, $labelOptions); $content = static::label(static::input('checkbox', $name, $value, $options) . ' ' . $label, null, $labelOptions);
return $hidden . static::tag('div', $content, ['class' => 'checkbox']); if (is_array($container)) {
return $hidden . static::tag('div', $content, $container);
} else {
return $hidden . $content;
}
} else { } else {
return $hidden . static::input('checkbox', $name, $value, $options); return $hidden . static::input('checkbox', $name, $value, $options);
} }
......
<?php <?php
namespace yiiunit\data\sphinx\ar; namespace yiiunit\data\ar\sphinx;
/** /**
* Test Sphinx ActiveRecord class * Test Sphinx ActiveRecord class
......
<?php <?php
namespace yiiunit\data\sphinx\ar; namespace yiiunit\data\ar\sphinx;
use yii\sphinx\ActiveRelation; use yii\sphinx\ActiveRelation;
use yiiunit\data\ar\ActiveRecord as ActiveRecordDb; use yiiunit\data\ar\ActiveRecord as ActiveRecordDb;
......
<?php <?php
namespace yiiunit\data\sphinx\ar; namespace yiiunit\data\ar\sphinx;
use yii\db\ActiveRelation; use yii\db\ActiveRelation;
......
<?php <?php
namespace yiiunit\data\sphinx\ar; namespace yiiunit\data\ar\sphinx;
use yiiunit\data\ar\ActiveRecord as ActiveRecordDb; use yiiunit\data\ar\ActiveRecord as ActiveRecordDb;
......
<?php <?php
namespace yiiunit\data\sphinx\ar; namespace yiiunit\data\ar\sphinx;
class ItemIndex extends ActiveRecord class ItemIndex extends ActiveRecord
{ {
......
<?php <?php
namespace yiiunit\data\sphinx\ar; namespace yiiunit\data\ar\sphinx;
class RuntimeIndex extends ActiveRecord class RuntimeIndex extends ActiveRecord
{ {
......
<?php <?php
namespace yiiunit\data\sphinx\ar; namespace yiiunit\data\ar\sphinx;
use yiiunit\data\ar\ActiveRecord as ActiveRecordDb; use yiiunit\data\ar\ActiveRecord as ActiveRecordDb;
......
...@@ -4,8 +4,8 @@ namespace yiiunit\extensions\sphinx; ...@@ -4,8 +4,8 @@ namespace yiiunit\extensions\sphinx;
use yii\data\ActiveDataProvider; use yii\data\ActiveDataProvider;
use yii\sphinx\Query; use yii\sphinx\Query;
use yiiunit\data\sphinx\ar\ActiveRecord; use yiiunit\data\ar\sphinx\ActiveRecord;
use yiiunit\data\sphinx\ar\ArticleIndex; use yiiunit\data\ar\sphinx\ArticleIndex;
/** /**
* @group sphinx * @group sphinx
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
namespace yiiunit\extensions\sphinx; namespace yiiunit\extensions\sphinx;
use yii\sphinx\ActiveQuery; use yii\sphinx\ActiveQuery;
use yiiunit\data\sphinx\ar\ActiveRecord; use yiiunit\data\ar\sphinx\ActiveRecord;
use yiiunit\data\sphinx\ar\ArticleIndex; use yiiunit\data\ar\sphinx\ArticleIndex;
use yiiunit\data\sphinx\ar\RuntimeIndex; use yiiunit\data\ar\sphinx\RuntimeIndex;
/** /**
* @group sphinx * @group sphinx
...@@ -41,10 +41,9 @@ class ActiveRecordTest extends SphinxTestCase ...@@ -41,10 +41,9 @@ class ActiveRecordTest extends SphinxTestCase
$this->assertTrue($articles[1] instanceof ArticleIndex); $this->assertTrue($articles[1] instanceof ArticleIndex);
// find fulltext // find fulltext
$articles = ArticleIndex::find('cats'); $article = ArticleIndex::find(2);
$this->assertEquals(1, count($articles)); $this->assertTrue($article instanceof ArticleIndex);
$this->assertTrue($articles[0] instanceof ArticleIndex); $this->assertEquals(2, $article->id);
$this->assertEquals(1, $articles[0]->id);
// find by column values // find by column values
$article = ArticleIndex::find(['id' => 2, 'author_id' => 2]); $article = ArticleIndex::find(['id' => 2, 'author_id' => 2]);
...@@ -168,7 +167,7 @@ class ActiveRecordTest extends SphinxTestCase ...@@ -168,7 +167,7 @@ class ActiveRecordTest extends SphinxTestCase
$record = RuntimeIndex::find(['id' => 2]); $record = RuntimeIndex::find(['id' => 2]);
$record->content = 'Test content with ' . $query; $record->content = 'Test content with ' . $query;
$record->save(); $record->save();
$rows = RuntimeIndex::find($query); $rows = RuntimeIndex::find()->match($query);
$this->assertNotEmpty($rows); $this->assertNotEmpty($rows);
// updateAll // updateAll
......
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
namespace yiiunit\extensions\sphinx; namespace yiiunit\extensions\sphinx;
use yiiunit\data\sphinx\ar\ActiveRecord; use yiiunit\data\ar\sphinx\ActiveRecord;
use yiiunit\data\ar\ActiveRecord as ActiveRecordDb; use yiiunit\data\ar\ActiveRecord as ActiveRecordDb;
use yiiunit\data\sphinx\ar\ArticleIndex; use yiiunit\data\ar\sphinx\ArticleIndex;
use yiiunit\data\sphinx\ar\ArticleDb; use yiiunit\data\ar\sphinx\ArticleDb;
/** /**
* @group sphinx * @group sphinx
......
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
namespace yiiunit\extensions\sphinx; namespace yiiunit\extensions\sphinx;
use yiiunit\data\sphinx\ar\ActiveRecord; use yiiunit\data\ar\sphinx\ActiveRecord;
use yiiunit\data\ar\ActiveRecord as ActiveRecordDb; use yiiunit\data\ar\ActiveRecord as ActiveRecordDb;
use yiiunit\data\sphinx\ar\ArticleIndex; use yiiunit\data\ar\sphinx\ArticleIndex;
use yiiunit\data\sphinx\ar\ArticleDb; use yiiunit\data\ar\sphinx\ArticleDb;
use yiiunit\data\sphinx\ar\TagDb; use yiiunit\data\ar\sphinx\TagDb;
/** /**
* @group sphinx * @group sphinx
......
...@@ -303,4 +303,21 @@ class ArrayHelperTest extends TestCase ...@@ -303,4 +303,21 @@ class ArrayHelperTest extends TestCase
], ],
], $result); ], $result);
} }
public function testKeyExists()
{
$array = [
'a' => 1,
'B' => 2,
];
$this->assertTrue(ArrayHelper::keyExists('a', $array));
$this->assertFalse(ArrayHelper::keyExists('b', $array));
$this->assertTrue(ArrayHelper::keyExists('B', $array));
$this->assertFalse(ArrayHelper::keyExists('c', $array));
$this->assertTrue(ArrayHelper::keyExists('a', $array, false));
$this->assertTrue(ArrayHelper::keyExists('b', $array, false));
$this->assertTrue(ArrayHelper::keyExists('B', $array, false));
$this->assertFalse(ArrayHelper::keyExists('c', $array, false));
}
} }
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