Commit ee0c9ed6 by Paul Klimov

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

parents d42a942a 3b5768b4
......@@ -13,13 +13,15 @@ nbproject
Thumbs.db
# composer vendor dir
/yii/vendor
/vendor
# composer itself is not needed
composer.phar
# composer.lock should not be committed as we always want the latest versions
/composer.lock
# Mac DS_Store Files
.DS_Store
# local phpunit config
/phpunit.xml
\ No newline at end of file
/phpunit.xml
......@@ -7,18 +7,22 @@ php:
services:
- redis-server
- memcached
- elasticsearch
before_script:
- composer self-update && composer --version
- composer require satooshi/php-coveralls 0.6.* --dev --prefer-dist
- mysql -e 'CREATE DATABASE yiitest;';
- mysql -D yiitest -u travis < /home/travis/build/yiisoft/yii2/tests/unit/data/sphinx/source.sql
- psql -U postgres -c 'CREATE DATABASE yiitest;';
- echo 'elasticsearch version ' && curl http://localhost:9200/
- tests/unit/data/travis/apc-setup.sh
- tests/unit/data/travis/memcache-setup.sh
- tests/unit/data/travis/cubrid-setup.sh
- tests/unit/data/travis/sphinx-setup.sh
script:
- phpunit --coverage-clover tests/unit/runtime/coveralls/clover.xml --verbose --exclude-group mssql,oci,wincache,xcache,zenddata,vendor,sphinx
#script:
# - phpunit --coverage-clover tests/unit/runtime/coveralls/clover.xml --verbose --exclude-group mssql,oci,wincache,xcache,zenddata,vendor,sphinx
after_script:
- php vendor/bin/coveralls
#after_script:
# - php vendor/bin/coveralls
......@@ -11,7 +11,7 @@ without prior notices. **Yii 2.0 is not ready for production use yet.**
[![Latest Stable Version](https://poser.pugx.org/yiisoft/yii2/v/stable.png)](https://packagist.org/packages/yiisoft/yii2)
[![Total Downloads](https://poser.pugx.org/yiisoft/yii2/downloads.png)](https://packagist.org/packages/yiisoft/yii2)
[![Build Status](https://secure.travis-ci.org/yiisoft/yii2.png)](http://travis-ci.org/yiisoft/yii2)
[![Build Status](https://secure.travis-ci.org/yiisoft/yii2.png)](http://travis-ci.org/yiisoft/yii2)
[![Dependency Status](https://www.versioneye.com/php/yiisoft:yii2/dev-master/badge.png)](https://www.versioneye.com/php/yiisoft:yii2/dev-master)
......@@ -24,7 +24,7 @@ DIRECTORY STRUCTURE
benchmark/ app demonstrating the minimal overhead introduced by the framework
build/ internally used build tools
docs/ documentation
extensions/ extensions
extensions/ extensions
framework/ framework files
yii/ framework source files
tests/ tests of the core framework code
......
......@@ -11,16 +11,16 @@ use yii\widgets\Breadcrumbs;
*/
AppAsset::register($this);
?>
<?php $this->beginPage(); ?>
<?php $this->beginPage() ?>
<!DOCTYPE html>
<html lang="<?= Yii::$app->language ?>">
<head>
<meta charset="<?= Yii::$app->charset ?>"/>
<title><?= Html::encode($this->title) ?></title>
<?php $this->head(); ?>
<?php $this->head() ?>
</head>
<body>
<?php $this->beginBody(); ?>
<?php $this->beginBody() ?>
<?php
NavBar::begin([
'brandLabel' => 'My Company',
......@@ -58,7 +58,7 @@ AppAsset::register($this);
</div>
</footer>
<?php $this->endBody(); ?>
<?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage(); ?>
<?php $this->endPage() ?>
......@@ -3,6 +3,7 @@
Yii::setAlias('common', realpath(__DIR__ . '/../'));
Yii::setAlias('frontend', realpath(__DIR__ . '/../../frontend'));
Yii::setAlias('backend', realpath(__DIR__ . '/../../backend'));
Yii::setAlias('console', realpath(__DIR__ . '/../../console'));
return [
'adminEmail' => 'admin@example.com',
......@@ -14,6 +15,7 @@ return [
'components.mail' => [
'class' => 'yii\swiftmailer\Mailer',
'viewPath' => '@common/mails',
],
'components.db' => [
......
<?php
use yii\helpers\Html;
use yii\mail\BaseMessage;
/**
* @var \yii\web\View $this
* @var BaseMessage $content
*/
?>
<?php $this->beginPage() ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=<?= Yii::$app->charset ?>" />
<title><?= Html::encode($this->title) ?></title>
<?php $this->head() ?>
</head>
<body>
<?php $this->beginBody() ?>
<?= $content ?>
<?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage() ?>
\ No newline at end of file
......@@ -6,19 +6,21 @@ class m130524_201442_init extends \yii\db\Migration
{
public function up()
{
// MySQL-specific table options. Adjust if you plan working with another DBMS
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=InnoDB';
$tableOptions = null;
if ($this->db->driverName === 'mysql') {
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=InnoDB';
}
$this->createTable('tbl_user', [
'id' => Schema::TYPE_PK,
'username' => Schema::TYPE_STRING.' NOT NULL',
'auth_key' => Schema::TYPE_STRING.'(32) NOT NULL',
'password_hash' => Schema::TYPE_STRING.' NOT NULL',
'password_reset_token' => Schema::TYPE_STRING.'(32)',
'email' => Schema::TYPE_STRING.' NOT NULL',
'role' => 'tinyint NOT NULL DEFAULT 10',
'username' => Schema::TYPE_STRING . ' NOT NULL',
'auth_key' => Schema::TYPE_STRING . '(32) NOT NULL',
'password_hash' => Schema::TYPE_STRING . ' NOT NULL',
'password_reset_token' => Schema::TYPE_STRING . '(32)',
'email' => Schema::TYPE_STRING . ' NOT NULL',
'role' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 10',
'status' => 'tinyint NOT NULL DEFAULT 10',
'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 10',
'create_time' => Schema::TYPE_INTEGER.' NOT NULL',
'update_time' => Schema::TYPE_INTEGER.' NOT NULL',
], $tableOptions);
......
......@@ -159,17 +159,11 @@ class SiteController extends Controller
$user->password_reset_token = Security::generateRandomKey();
if ($user->save(false)) {
// todo: refactor it with mail component. pay attention to the arrangement of mail view files
$fromEmail = \Yii::$app->params['supportEmail'];
$name = '=?UTF-8?B?' . base64_encode(\Yii::$app->name . ' robot') . '?=';
$subject = '=?UTF-8?B?' . base64_encode('Password reset for ' . \Yii::$app->name) . '?=';
$body = $this->renderPartial('/emails/passwordResetToken', [
'user' => $user,
]);
$headers = "From: $name <{$fromEmail}>\r\n" .
"MIME-Version: 1.0\r\n" .
"Content-type: text/plain; charset=UTF-8";
return mail($email, $subject, $body, $headers);
return \Yii::$app->mail->compose('passwordResetToken', ['user' => $user])
->setFrom([\Yii::$app->params['supportEmail'] => \Yii::$app->name . ' robot'])
->setTo($email)
->setSubject('Password reset for ' . \Yii::$app->name)
->send();
}
return false;
......
......@@ -12,16 +12,16 @@ use frontend\widgets\Alert;
*/
AppAsset::register($this);
?>
<?php $this->beginPage(); ?>
<?php $this->beginPage() ?>
<!DOCTYPE html>
<html lang="<?= Yii::$app->language ?>">
<head>
<meta charset="<?= Yii::$app->charset ?>"/>
<title><?= Html::encode($this->title) ?></title>
<?php $this->head(); ?>
<?php $this->head() ?>
</head>
<body>
<?php $this->beginBody(); ?>
<?php $this->beginBody() ?>
<?php
NavBar::begin([
'brandLabel' => 'My Company',
......@@ -63,7 +63,7 @@ AppAsset::register($this);
</div>
</footer>
<?php $this->endBody(); ?>
<?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage(); ?>
<?php $this->endPage() ?>
......@@ -27,13 +27,13 @@ class Alert extends \yii\bootstrap\Widget
* - $value is the bootstrap alert type (i.e. danger, success, info, warning)
*/
public $alertTypes = [
'error' => 'danger',
'danger' => 'danger',
'success' => 'success',
'info' => 'info',
'error' => 'danger',
'danger' => 'danger',
'success' => 'success',
'info' => 'info',
'warning' => 'warning'
];
/**
* @var array the options for rendering the close button tag.
*/
......@@ -49,7 +49,7 @@ class Alert extends \yii\bootstrap\Widget
foreach ($flashes as $type => $message) {
/* initialize css class for each alert box */
$this->options['class'] = 'alert-' . $this->alertTypes[$type] . $appendCss;
$this->options['class'] = 'alert-' . $this->alertTypes[$type] . $appendCss;
/* assign unique id to each alert box */
$this->options['id'] = $this->getId() . '-' . $type;
......
......@@ -13,7 +13,7 @@ use yii\console\Controller;
* This command echoes what the first argument that you have entered.
*
* This command is provided as an example for you to learn how to create console commands.
*
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
......
<?php
namespace Codeception\Module;
// here you can define custom functions for CodeGuy
// here you can define custom functions for CodeGuy
class CodeHelper extends \Codeception\Module
{
......
<?php
namespace Codeception\Module;
// here you can define custom functions for TestGuy
// here you can define custom functions for TestGuy
class TestHelper extends \Codeception\Module
{
......
<?php
namespace Codeception\Module;
// here you can define custom functions for WebGuy
// here you can define custom functions for WebGuy
class WebHelper extends \Codeception\Module
{
......
......@@ -25,6 +25,6 @@ use Codeception\Module\CodeHelper;
class CodeGuy extends \Codeception\AbstractGuy
{
}
......@@ -11,16 +11,16 @@ use app\assets\AppAsset;
*/
AppAsset::register($this);
?>
<?php $this->beginPage(); ?>
<?php $this->beginPage() ?>
<!DOCTYPE html>
<html lang="<?= Yii::$app->language ?>">
<head>
<meta charset="<?= Yii::$app->charset ?>"/>
<title><?= Html::encode($this->title) ?></title>
<?php $this->head(); ?>
<?php $this->head() ?>
</head>
<body>
<?php $this->beginBody(); ?>
<?php $this->beginBody() ?>
<?php
NavBar::begin([
'brandLabel' => 'My Company',
......@@ -59,7 +59,7 @@ AppAsset::register($this);
</div>
</footer>
<?php $this->endBody(); ?>
<?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage(); ?>
<?php $this->endPage() ?>
......@@ -11,6 +11,8 @@
// fcgi doesn't have STDIN defined by default
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
define('YII_DEBUG', true);
require(__DIR__ . '/../framework/yii/Yii.php');
$application = new yii\console\Application([
......
......@@ -7,6 +7,7 @@
namespace yii\build\controllers;
use Yii;
use yii\console\Controller;
use yii\helpers\Console;
use yii\helpers\FileHelper;
......@@ -38,10 +39,30 @@ class PhpDocController extends Controller
*
* @param null $root the directory to parse files from. Defaults to YII_PATH.
*/
public function actionProperty($root=null)
public function actionProperty($root = null)
{
$except = [];
if ($root === null) {
$root = YII_PATH;
$root = dirname(dirname(YII_PATH));
Yii::setAlias('@yii/bootstrap', $root . '/extensions/bootstrap');
Yii::setAlias('@yii/debug', $root . '/extensions/debug');
Yii::setAlias('@yii/elasticsearch', $root . '/extensions/elasticsearch');
Yii::setAlias('@yii/gii', $root . '/extensions/gii');
Yii::setAlias('@yii/jui', $root . '/extensions/jui');
Yii::setAlias('@yii/redis', $root . '/extensions/redis');
Yii::setAlias('@yii/smarty', $root . '/extensions/smarty');
Yii::setAlias('@yii/sphinx', $root . '/extensions/sphinx');
Yii::setAlias('@yii/swiftmailer', $root . '/extensions/swiftmailer');
Yii::setAlias('@yii/twig', $root . '/extensions/twig');
$except = [
'/apps/',
'/build/',
'/docs/',
'/extensions/composer/',
'/tests/',
'/vendor/',
];
}
$root = FileHelper::normalizePath($root);
$options = [
......@@ -55,14 +76,13 @@ class PhpDocController extends Controller
return null;
},
'only' => ['.php'],
'except' => [
'except' => array_merge($except, [
'BaseYii.php',
'Yii.php',
'/debug/views/',
'/views/',
'/requirements/',
'/gii/views/',
'/gii/generators/',
],
]),
];
$files = FileHelper::findFiles($root, $options);
$nFilesTotal = 0;
......@@ -216,20 +236,27 @@ class PhpDocController extends Controller
$ns = $this->match('#\nnamespace (?<name>[\w\\\\]+);\n#', $file);
$namespace = reset($ns);
$namespace = $namespace['name'];
$classes = $this->match('#\n(?:abstract )?class (?<name>\w+)( |\n)(extends )?.+\{(?<content>.*)\n\}(\n|$)#', $file);
$classes = $this->match('#\n(?:abstract )?class (?<name>\w+)( extends .+)?( implements .+)?\n\{(?<content>.*)\n\}(\n|$)#', $file);
if (count($classes) > 1) {
$this->stderr("[ERR] There should be only one class in a file: $fileName\n", Console::FG_RED);
return false;
}
if (count($classes) < 1) {
$interfaces = $this->match('#\ninterface (?<name>\w+)\n\{(?<content>.+)\n\}(\n|$)#', $file);
$interfaces = $this->match('#\ninterface (?<name>\w+)( extends .+)?\n\{(?<content>.+)\n\}(\n|$)#', $file);
if (count($interfaces) == 1) {
return false;
} elseif (count($interfaces) > 1) {
$this->stderr("[ERR] There should be only one interface in a file: $fileName\n", Console::FG_RED);
} else {
$this->stderr("[ERR] No class in file: $fileName\n", Console::FG_RED);
$traits = $this->match('#\ntrait (?<name>\w+)\n\{(?<content>.+)\n\}(\n|$)#', $file);
if (count($traits) == 1) {
return false;
} elseif (count($traits) > 1) {
$this->stderr("[ERR] There should be only one class/trait/interface in a file: $fileName\n", Console::FG_RED);
} else {
$this->stderr("[ERR] No class in file: $fileName\n", Console::FG_RED);
}
}
return false;
}
......
......@@ -3,7 +3,7 @@ Active Record
Active Record implements the [Active Record design pattern](http://en.wikipedia.org/wiki/Active_record).
The premise behind Active Record is that an individual [[ActiveRecord]] object is associated with a specific row in a database table. The object's attributes are mapped to the columns of the corresponding table. Referencing an Active Record attribute is equivalent to accessing
the corresponding table column for that record.
the corresponding table column for that record.
As an example, say that the `Customer` ActiveRecord class is associated with the
`tbl_customer` table. This would mean that the class's `name` attribute is automatically mapped to the `name` column in `tbl_customer`.
......
......@@ -3,7 +3,7 @@ Authentication
Authentication is the act of verifying who a user is, and is the basis of the login process. Typically, authentication uses an identifier--a username or email address--and password, submitted through a form. The application then compares this information against that previously stored.
In Yii all this is done semi-automatically, leaving the developer to merely implement [[\yii\web\IdentityInterface]]. Typically, implementation is accomplished using the `User` model. You can find a full featured example in the
In Yii all this is done semi-automatically, leaving the developer to merely implement [[\yii\web\IdentityInterface]]. Typically, implementation is accomplished using the `User` model. You can find a full featured example in the
[advanced application template](installation.md). Below only the interface methods are listed:
```php
......
......@@ -9,7 +9,7 @@ Installing Composer
In order to install Composer, check the official guide for your operating system:
* [Linux](http://getcomposer.org/doc/00-intro.md#installation-nix)
* [Linux](http://getcomposer.org/doc/00-intro.md#installation-nix)
* [Windows](http://getcomposer.org/doc/00-intro.md#installation-windows)
All of the details can be found in the guide, but you'll either download Composer directly from [http://getcomposer.org/](http://getcomposer.org/), or run the following command:
......
......@@ -167,7 +167,7 @@ public SiteController extends \yii\web\Controller
{
return [
'about' => [
'class' => '@app/actions/Page',
'class' => 'app\actions\Page',
'view' => 'about',
],
];
......
......@@ -88,3 +88,19 @@ customize the output, you can chain additional methods to this call:
<?= $form->field($model, 'username')->textInput()->hint('Please enter your name')->label('Name') ?>
```
This will create all the `<label>`, `<input>` and other tags according to the template defined by the form field.
To add these tags yourself you can use the `Html` helper class. The following is equivalent to the code above:
```php
<?= Html::activeLabel($model, 'password') ?>
<?= Html::activePasswordInput($model, 'password') ?>
<?= Html::error($model, 'password') ?>
or
<?= Html::activeLabel($model, 'username', ['label' => 'name']) ?>
<?= Html::activeTextInput($model, 'username') ?>
<?= Html::error($model, 'username') ?>
<div class="hint-block">Please enter your name</div>
```
......@@ -19,7 +19,7 @@ curl -s http://getcomposer.org/installer | php
For problems or more information, see the official Composer guide:
* [Linux](http://getcomposer.org/doc/00-intro.md#installation-nix)
* [Linux](http://getcomposer.org/doc/00-intro.md#installation-nix)
* [Windows](http://getcomposer.org/doc/00-intro.md#installation-windows)
With Composer installed, you can create a new Yii site using one of Yii's ready-to-use application templates.
......
......@@ -59,7 +59,7 @@ Attribute Labels
----------------
Attribute labels are mainly used for display purpose. For example, given an attribute `firstName`, we can declare
a label `First Name` that is more user-friendly when displayed to end users in places such as form labels and
a label `First Name` that is more user-friendly when displayed to end users in places such as form labels and
error messages. Given an attribute name, you can obtain its label by calling [[\yii\base\Model::getAttributeLabel()]].
To declare attribute labels, override the [[\yii\base\Model::attributeLabels()]] method. The overridden method returns a mapping of attribute names to attribute labels, as shown in the example below. If an attribute is not found
......@@ -86,7 +86,7 @@ Scenarios
---------
A model may be used in different *scenarios*. For example, a `User` model may be used to collect user login inputs,
but it may also be used for user registration purposes. In the one scenario, every piece of data is required; in the other, only the username and password would be.
but it may also be used for user registration purposes. In the one scenario, every piece of data is required; in the other, only the username and password would be.
To easily implement the business logic for different scenarios, each model has a property named `scenario`
that stores the name of the scenario that the model is currently being used in. As will be explained in the next
......
......@@ -3,7 +3,7 @@ MVC Overview
Yii implements the model-view-controller (MVC) design pattern, which is
widely adopted in web and other application programming. MVC aims to separate business logic from
user interface considerations, allowing developers to more easily change individual components of an application without affecting, or even touching, another.
user interface considerations, allowing developers to more easily change individual components of an application without affecting, or even touching, another.
In MVC, the *model* represents the
information (the data) and the business rules to which the data must adhere. The *view* contains elements
......
Security
========
Good security is vital to the health and success of many websites. Unfortunately, many developers may cut corners when it comes to security due to a lack of understanding or too large of an implementation hurdle. To make your Yii-based site as secure as possible, the Yii framework has baked in several excellent, and easy to use, security features.
Good security is vital to the health and success of many websites. Unfortunately, many developers may cut corners when it comes to security due to a lack of understanding or too large of an implementation hurdle. To make your Yii-based site as secure as possible, the Yii framework has baked in several excellent, and easy to use, security features.
Hashing and verifying passwords
-------------------------------
......
Using template engines
======================
By default Yii uses PHP as template language, but you can configure it to support other rendering engines, such as [Twig](http://twig.sensiolabs.org/) or [Smarty](http://www.smarty.net/).
By default Yii uses PHP as template language, but you can configure it 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 component's behavior:
The `view` component is responsible for rendering views. You can add a custom template engines by reconfiguring this
component's behavior:
```php
[
......@@ -13,11 +14,13 @@ a custom template engines by reconfiguring this component's behavior:
'class' => 'yii\web\View',
'renderers' => [
'tpl' => [
'class' => 'yii\renderers\SmartyViewRenderer',
'class' => 'yii\smarty\ViewRenderer',
//'cachePath' => '@runtime/Smarty/cache',
],
'twig' => [
'class' => 'yii\renderers\TwigViewRenderer',
'twigPath' => '@app/vendors/Twig',
'class' => 'yii\twig\ViewRenderer',
//'cachePath' => '@runtime/Twig/cache',
//'options' => [], /* Array of twig options */
],
// ...
],
......@@ -26,7 +29,15 @@ a custom template engines by reconfiguring this component's behavior:
]
```
Note that the Smarty and Twig packages themselves are not bundled with Yii. You must download them yourself. Then unpack the packages and place the resulting files in a logical location, such as the application's `protected/vendor` folder. Finally, specify the correct `smartyPath` or `twigPath`, as in the code above (for Twig).
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
```
"yiisoft/yii2-smarty": "*",
"yiisoft/yii2-twig": "*",
```
in `require` section and then run `composer update`.
Twig
----
......
......@@ -135,7 +135,7 @@ Path alias is also closely related with class namespaces. It is recommended that
alias be defined for each root namespace so that you can use Yii the class autoloader without
any further configuration. For example, because `@yii` refers to the Yii installation directory,
a class like `yii\web\Request` can be autoloaded by Yii. If you use a third party library
such as Zend Framework, you may define a path alias `@Zend` which refers to its installation
such as Zend Framework, you may define a path alias `@Zend` which refers to its installation
directory and Yii will be able to autoload any class in this library.
More on path aliases can be found in the [Basic concepts section](basics.md).
......
......@@ -254,24 +254,24 @@ basic one without any widgets or extra markup.
<?php
use yii\helpers\Html;
?>
<?php $this->beginPage(); ?>
<?php $this->beginPage() ?>
<!DOCTYPE html>
<html lang="<?= Yii::$app->language ?>">
<head>
<meta charset="<?= Yii::$app->charset ?>"/>
<title><?= Html::encode($this->title) ?></title>
<?php $this->head(); ?>
<?php $this->head() ?>
</head>
<body>
<?php $this->beginBody(); ?>
<?php $this->beginBody() ?>
<div class="container">
<?= $content ?>
</div>
<footer class="footer">© 2013 me :)</footer>
<?php $this->endBody(); ?>
<?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage(); ?>
<?php $this->endPage() ?>
```
In the markup above there's some code. First of all, `$content` is a variable that will contain result of views rendered
......
......@@ -14,7 +14,7 @@ use yii\helpers\Html;
/**
* Nav renders a nav HTML component.
*
*
* For example:
*
* ```php
......@@ -37,12 +37,12 @@ use yii\helpers\Html;
* ],
* ]);
* ```
*
*
* Note: Multilevel dropdowns beyond Level 1 are not supported in Bootstrap 3.
*
*
* @see http://getbootstrap.com/components.html#dropdowns
* @see http://getbootstrap.com/components/#nav
*
*
* @author Antonio Ramirez <amigo.cobos@gmail.com>
* @since 2.0
*/
......
......@@ -5,7 +5,7 @@
"type": "yii2-extension",
"license": "BSD-3-Clause",
"support": {
"issues": "https://github.com/yiisoft/yii2/issues?state=open",
"issues": "https://github.com/yiisoft/yii2/issues?labels=ext%3Abootstrap",
"forum": "http://www.yiiframework.com/forum/",
"wiki": "http://www.yiiframework.com/wiki/",
"irc": "irc://irc.freenode.net/yii",
......
......@@ -26,7 +26,7 @@ class Installer extends LibraryInstaller
const EXTENSION_FILE = 'yiisoft/extensions.php';
/**
* @inheritdoc
* {@inheritdoc}
*/
public function supports($packageType)
{
......@@ -34,7 +34,7 @@ class Installer extends LibraryInstaller
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
{
......@@ -49,7 +49,7 @@ class Installer extends LibraryInstaller
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
{
......@@ -63,7 +63,7 @@ class Installer extends LibraryInstaller
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
{
......
......@@ -20,7 +20,7 @@ use Composer\Plugin\PluginInterface;
class Plugin implements PluginInterface
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public function activate(Composer $composer, IOInterface $io)
{
......
......@@ -5,7 +5,7 @@
"type": "yii2-extension",
"license": "BSD-3-Clause",
"support": {
"issues": "https://github.com/yiisoft/yii2/issues?state=open",
"issues": "https://github.com/yiisoft/yii2/issues?labels=ext%3Adebug",
"forum": "http://www.yiiframework.com/forum/",
"wiki": "http://www.yiiframework.com/wiki/",
"irc": "irc://irc.freenode.net/yii",
......
......@@ -9,15 +9,15 @@ yii\debug\DebugAsset::register($this);
?>
<!DOCTYPE html>
<html>
<?php $this->beginPage(); ?>
<?php $this->beginPage() ?>
<head>
<title><?= Html::encode($this->title) ?></title>
<?php $this->head(); ?>
<?php $this->head() ?>
</head>
<body>
<?php $this->beginBody(); ?>
<?php $this->beginBody() ?>
<?= $content ?>
<?php $this->endBody(); ?>
<?php $this->endBody() ?>
</body>
<?php $this->endPage(); ?>
<?php $this->endPage() ?>
</html>
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\elasticsearch;
use yii\db\ActiveQueryInterface;
use yii\db\ActiveQueryTrait;
/**
* ActiveQuery represents a [[Query]] associated with an [[ActiveRecord]] class.
*
* ActiveQuery instances are usually created by [[ActiveRecord::find()]].
*
* ActiveQuery mainly provides the following methods to retrieve the query results:
*
* - [[one()]]: returns a single record populated with the first row of data.
* - [[all()]]: returns all records based on the query results.
* - [[count()]]: returns the number of records.
* - [[scalar()]]: returns the value of the first column in the first row of the query result.
* - [[column()]]: returns the value of the first column in the query result.
* - [[exists()]]: returns a value indicating whether the query result has data or not.
*
* Because ActiveQuery extends from [[Query]], one can use query methods, such as [[where()]],
* [[orderBy()]] to customize the query options.
*
* ActiveQuery also provides the following additional query options:
*
* - [[with()]]: list of relations that this query should be performed with.
* - [[indexBy()]]: the name of the column by which the query result should be indexed.
* - [[asArray()]]: whether to return each record as an array.
*
* These options can be configured using methods of the same name. For example:
*
* ~~~
* $customers = Customer::find()->with('orders')->asArray()->all();
* ~~~
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class ActiveQuery extends Query implements ActiveQueryInterface
{
use ActiveQueryTrait;
/**
* Creates a DB command that can be used to execute this query.
* @param Connection $db the DB connection used to create the DB command.
* If null, the DB connection returned by [[modelClass]] will be used.
* @return Command the created DB command instance.
*/
public function createCommand($db = null)
{
/** @var ActiveRecord $modelClass */
$modelClass = $this->modelClass;
if ($db === null) {
$db = $modelClass::getDb();
}
if ($this->type === null) {
$this->type = $modelClass::type();
}
if ($this->index === null) {
$this->index = $modelClass::index();
$this->type = $modelClass::type();
}
$commandConfig = $db->getQueryBuilder()->build($this);
return $db->createCommand($commandConfig);
}
/**
* Executes query and returns all results as an array.
* @param Connection $db the DB connection used to create the DB command.
* If null, the DB connection returned by [[modelClass]] will be used.
* @return array the query results. If the query results in nothing, an empty array will be returned.
*/
public function all($db = null)
{
$result = $this->createCommand($db)->search();
if (empty($result['hits']['hits'])) {
return [];
}
if ($this->fields !== null) {
foreach ($result['hits']['hits'] as &$row) {
$row['_source'] = isset($row['fields']) ? $row['fields'] : [];
unset($row['fields']);
}
unset($row);
}
if ($this->asArray && $this->indexBy) {
foreach ($result['hits']['hits'] as &$row) {
$row['_source'][ActiveRecord::PRIMARY_KEY_NAME] = $row['_id'];
$row = $row['_source'];
}
}
$models = $this->createModels($result['hits']['hits']);
if ($this->asArray && !$this->indexBy) {
foreach($models as $key => $model) {
$model['_source'][ActiveRecord::PRIMARY_KEY_NAME] = $model['_id'];
$models[$key] = $model['_source'];
}
}
if (!empty($this->with)) {
$this->findWith($this->with, $models);
}
return $models;
}
/**
* Executes query and returns a single row of result.
* @param Connection $db the DB connection used to create the DB command.
* If null, the DB connection returned by [[modelClass]] will be used.
* @return ActiveRecord|array|null a single row of query result. Depending on the setting of [[asArray]],
* the query result may be either an array or an ActiveRecord object. Null will be returned
* if the query results in nothing.
*/
public function one($db = null)
{
if (($result = parent::one($db)) === false) {
return null;
}
if ($this->asArray) {
$model = $result['_source'];
$model[ActiveRecord::PRIMARY_KEY_NAME] = $result['_id'];
} else {
/** @var ActiveRecord $class */
$class = $this->modelClass;
$model = $class::create($result);
}
if (!empty($this->with)) {
$models = [$model];
$this->findWith($this->with, $models);
$model = $models[0];
}
return $model;
}
/**
* {@inheritdoc}
*/
public function search($db = null, $options = [])
{
$result = $this->createCommand($db)->search($options);
if (!empty($result['hits']['hits'])) {
$models = $this->createModels($result['hits']['hits']);
if ($this->asArray) {
foreach($models as $key => $model) {
$model['_source'][ActiveRecord::PRIMARY_KEY_NAME] = $model['_id'];
$models[$key] = $model['_source'];
}
}
if (!empty($this->with)) {
$this->findWith($this->with, $models);
}
$result['hits']['hits'] = $models;
}
return $result;
}
/**
* {@inheritdoc}
*/
public function scalar($field, $db = null)
{
$record = parent::one($db);
if ($record !== false) {
if ($field == ActiveRecord::PRIMARY_KEY_NAME) {
return $record['_id'];
} elseif (isset($record['_source'][$field])) {
return $record['_source'][$field];
}
}
return null;
}
/**
* {@inheritdoc}
*/
public function column($field, $db = null)
{
if ($field == ActiveRecord::PRIMARY_KEY_NAME) {
$command = $this->createCommand($db);
$command->queryParts['fields'] = [];
$result = $command->search();
if (empty($result['hits']['hits'])) {
return [];
}
$column = [];
foreach ($result['hits']['hits'] as $row) {
$column[] = $row['_id'];
}
return $column;
}
return parent::column($field, $db);
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\elasticsearch;
use yii\db\ActiveRelationInterface;
use yii\db\ActiveRelationTrait;
/**
* ActiveRelation represents a relation between two Active Record classes.
*
* ActiveRelation instances are usually created by calling [[ActiveRecord::hasOne()]] and
* [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
* a getter method which calls one of the above methods and returns the created ActiveRelation object.
*
* A relation is specified by [[link]] which represents the association between columns
* of different tables; and the multiplicity of the relation is indicated by [[multiple]].
*
* If a relation involves a pivot table, it may be specified by [[via()]] method.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class ActiveRelation extends ActiveQuery implements ActiveRelationInterface
{
use ActiveRelationTrait;
/**
* Creates a DB command that can be used to execute this query.
* @param Connection $db the DB connection used to create the DB command.
* If null, the DB connection returned by [[modelClass]] will be used.
* @return Command the created DB command instance.
*/
public function createCommand($db = null)
{
if ($this->primaryModel !== null) {
// lazy loading
if (is_array($this->via)) {
// via relation
/** @var ActiveRelation $viaQuery */
list($viaName, $viaQuery) = $this->via;
if ($viaQuery->multiple) {
$viaModels = $viaQuery->all();
$this->primaryModel->populateRelation($viaName, $viaModels);
} else {
$model = $viaQuery->one();
$this->primaryModel->populateRelation($viaName, $model);
$viaModels = $model === null ? [] : [$model];
}
$this->filterByModels($viaModels);
} else {
$this->filterByModels([$this->primaryModel]);
}
}
return parent::createCommand($db);
}
}
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\elasticsearch;
use Yii;
use yii\base\Component;
use yii\base\InvalidConfigException;
use yii\helpers\Json;
/**
* elasticsearch Connection is used to connect to an elasticsearch cluster version 0.20 or higher
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class Connection extends Component
{
/**
* @event Event an event that is triggered after a DB connection is established
*/
const EVENT_AFTER_OPEN = 'afterOpen';
/**
* @var bool whether to autodetect available cluster nodes on [[open()]]
*/
public $autodetectCluster = true;
/**
* @var array cluster nodes
* This is populated with the result of a cluster nodes request when [[autodetectCluster]] is true.
* @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/cluster-nodes-info.html#cluster-nodes-info
*/
public $nodes = [
['http_address' => 'inet[/127.0.0.1:9200]'],
];
/**
* @var array the active node. key of [[nodes]]. Will be randomly selected on [[open()]].
*/
public $activeNode;
// TODO http://www.elasticsearch.org/guide/en/elasticsearch/client/php-api/current/_configuration.html#_example_configuring_http_basic_auth
public $auth = [];
/**
* @var float timeout to use for connecting to an elasticsearch node.
* This value will be used to configure the curl `CURLOPT_CONNECTTIMEOUT` option.
* If not set, no explicit timeout will be set for curl.
*/
public $connectionTimeout = null;
/**
* @var float timeout to use when reading the response from an elasticsearch node.
* This value will be used to configure the curl `CURLOPT_TIMEOUT` option.
* If not set, no explicit timeout will be set for curl.
*/
public $dataTimeout = null;
public function init()
{
foreach($this->nodes as $node) {
if (!isset($node['http_address'])) {
throw new InvalidConfigException('Elasticsearch node needs at least a http_address configured.');
}
}
}
/**
* Closes the connection when this component is being serialized.
* @return array
*/
public function __sleep()
{
$this->close();
return array_keys(get_object_vars($this));
}
/**
* Returns a value indicating whether the DB connection is established.
* @return boolean whether the DB connection is established
*/
public function getIsActive()
{
return $this->activeNode !== null;
}
/**
* Establishes a DB connection.
* It does nothing if a DB connection has already been established.
* @throws Exception if connection fails
*/
public function open()
{
if ($this->activeNode !== null) {
return;
}
if (empty($this->nodes)) {
throw new InvalidConfigException('elasticsearch needs at least one node to operate.');
}
if ($this->autodetectCluster) {
$node = reset($this->nodes);
$host = $node['http_address'];
if (strncmp($host, 'inet[/', 6) == 0) {
$host = substr($host, 6, -1);
}
$response = $this->httpRequest('GET', 'http://' . $host . '/_cluster/nodes');
$this->nodes = $response['nodes'];
if (empty($this->nodes)) {
throw new Exception('cluster autodetection did not find any active node.');
}
}
$this->selectActiveNode();
Yii::trace('Opening connection to elasticsearch. Nodes in cluster: ' . count($this->nodes)
. ', active node: ' . $this->nodes[$this->activeNode]['http_address'], __CLASS__);
$this->initConnection();
}
/**
* select active node randomly
*/
protected function selectActiveNode()
{
$keys = array_keys($this->nodes);
$this->activeNode = $keys[rand(0, count($keys) - 1)];
}
/**
* Closes the currently active DB connection.
* It does nothing if the connection is already closed.
*/
public function close()
{
Yii::trace('Closing connection to elasticsearch. Active node was: '
. $this->nodes[$this->activeNode]['http_address'], __CLASS__);
$this->activeNode = null;
}
/**
* Initializes the DB connection.
* This method is invoked right after the DB connection is established.
* The default implementation triggers an [[EVENT_AFTER_OPEN]] event.
*/
protected function initConnection()
{
$this->trigger(self::EVENT_AFTER_OPEN);
}
/**
* Returns the name of the DB driver for the current [[dsn]].
* @return string name of the DB driver
*/
public function getDriverName()
{
return 'elasticsearch';
}
/**
* Creates a command for execution.
* @param array $config the configuration for the Command class
* @return Command the DB command
*/
public function createCommand($config = [])
{
$this->open();
$config['db'] = $this;
$command = new Command($config);
return $command;
}
public function getQueryBuilder()
{
return new QueryBuilder($this);
}
public function get($url, $options = [], $body = null)
{
$this->open();
return $this->httpRequest('GET', $this->createUrl($url, $options), $body);
}
public function head($url, $options = [], $body = null)
{
$this->open();
return $this->httpRequest('HEAD', $this->createUrl($url, $options), $body);
}
public function post($url, $options = [], $body = null)
{
$this->open();
return $this->httpRequest('POST', $this->createUrl($url, $options), $body);
}
public function put($url, $options = [], $body = null)
{
$this->open();
return $this->httpRequest('PUT', $this->createUrl($url, $options), $body);
}
public function delete($url, $options = [], $body = null)
{
$this->open();
return $this->httpRequest('DELETE', $this->createUrl($url, $options), $body);
}
private function createUrl($path, $options = [])
{
$url = implode('/', array_map(function($a) {
return urlencode(is_array($a) ? implode(',', $a) : $a);
}, $path));
if (!empty($options)) {
$url .= '?' . http_build_query($options);
}
return [$this->nodes[$this->activeNode]['http_address'], $url];
}
protected function httpRequest($method, $url, $requestBody = null)
{
$method = strtoupper($method);
// response body and headers
$headers = [];
$body = '';
$options = [
CURLOPT_USERAGENT => 'Yii2 Framework ' . __CLASS__,
CURLOPT_RETURNTRANSFER => false,
CURLOPT_HEADER => false,
// http://www.php.net/manual/en/function.curl-setopt.php#82418
CURLOPT_HTTPHEADER => ['Expect:'],
CURLOPT_WRITEFUNCTION => function($curl, $data) use (&$body) {
$body .= $data;
return mb_strlen($data, '8bit');
},
CURLOPT_HEADERFUNCTION => function($curl, $data) use (&$headers) {
foreach(explode("\r\n", $data) as $row) {
if (($pos = strpos($row, ':')) !== false) {
$headers[strtolower(substr($row, 0, $pos))] = trim(substr($row, $pos + 1));
}
}
return mb_strlen($data, '8bit');
},
CURLOPT_CUSTOMREQUEST => $method,
];
if ($this->connectionTimeout !== null) {
$options[CURLOPT_CONNECTTIMEOUT] = $this->connectionTimeout;
}
if ($this->dataTimeout !== null) {
$options[CURLOPT_TIMEOUT] = $this->dataTimeout;
}
if ($requestBody !== null) {
$options[CURLOPT_POSTFIELDS] = $requestBody;
}
if ($method == 'HEAD') {
$options[CURLOPT_NOBODY] = true;
unset($options[CURLOPT_WRITEFUNCTION]);
}
if (is_array($url)) {
list($host, $q) = $url;
if (strncmp($host, 'inet[/', 6) == 0) {
$host = substr($host, 6, -1);
}
$profile = $q . $requestBody;
$url = 'http://' . $host . '/' . $q;
} else {
$profile = false;
}
Yii::trace("Sending request to elasticsearch node: $url\n$requestBody", __METHOD__);
if ($profile !== false) {
Yii::beginProfile($profile, __METHOD__);
}
$curl = curl_init($url);
curl_setopt_array($curl, $options);
if (curl_exec($curl) === false) {
throw new Exception('Elasticsearch request failed: ' . curl_errno($curl) . ' - ' . curl_error($curl), [
'requestMethod' => $method,
'requestUrl' => $url,
'requestBody' => $requestBody,
'responseHeaders' => $headers,
'responseBody' => $body,
]);
}
$responseCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
if ($profile !== false) {
Yii::endProfile($profile, __METHOD__);
}
if ($responseCode >= 200 && $responseCode < 300) {
if ($method == 'HEAD') {
return true;
} else {
if (isset($headers['content-length']) && ($len = mb_strlen($body, '8bit')) < $headers['content-length']) {
throw new Exception("Incomplete data received from elasticsearch: $len < {$headers['content-length']}", [
'requestMethod' => $method,
'requestUrl' => $url,
'requestBody' => $requestBody,
'responseCode' => $responseCode,
'responseHeaders' => $headers,
'responseBody' => $body,
]);
}
if (isset($headers['content-type']) && !strncmp($headers['content-type'], 'application/json', 16)) {
return Json::decode($body);
}
throw new Exception('Unsupported data received from elasticsearch: ' . $headers['content-type'], [
'requestMethod' => $method,
'requestUrl' => $url,
'requestBody' => $requestBody,
'responseCode' => $responseCode,
'responseHeaders' => $headers,
'responseBody' => $body,
]);
}
} elseif ($responseCode == 404) {
return false;
} else {
throw new Exception("Elasticsearch request failed with code $responseCode.", [
'requestMethod' => $method,
'requestUrl' => $url,
'requestBody' => $requestBody,
'responseCode' => $responseCode,
'responseHeaders' => $headers,
'responseBody' => $body,
]);
}
}
public function getNodeInfo()
{
return $this->get([]);
}
public function getClusterState()
{
return $this->get(['_cluster', 'state']);
}
}
\ No newline at end of file
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\elasticsearch;
/**
* Exception represents an exception that is caused by elasticsearch-related operations.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class Exception extends \yii\db\Exception
{
/**
* @var array additional information about the http request that caused the error.
*/
public $errorInfo = [];
/**
* Constructor.
* @param string $message error message
* @param array $errorInfo error info
* @param integer $code error code
* @param \Exception $previous The previous exception used for the exception chaining.
*/
public function __construct($message, $errorInfo = [], $code = 0, \Exception $previous = null)
{
$this->errorInfo = $errorInfo;
parent::__construct($message, $code, $previous);
}
/**
* @return string the user-friendly name of this exception
*/
public function getName()
{
return \Yii::t('yii', 'Elasticsearch Database Exception');
}
}
\ No newline at end of file
The Yii framework is free software. It is released under the terms of
the following BSD License.
Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Yii Software LLC nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\elasticsearch;
use yii\base\InvalidParamException;
use yii\base\NotSupportedException;
use yii\helpers\Json;
/**
* QueryBuilder builds an elasticsearch query based on the specification given as a [[Query]] object.
*
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class QueryBuilder extends \yii\base\Object
{
/**
* @var Connection the database connection.
*/
public $db;
/**
* Constructor.
* @param Connection $connection the database connection.
* @param array $config name-value pairs that will be used to initialize the object properties
*/
public function __construct($connection, $config = [])
{
$this->db = $connection;
parent::__construct($config);
}
/**
* Generates query from a [[Query]] object.
* @param Query $query the [[Query]] object from which the query will be generated
* @return array the generated SQL statement (the first array element) and the corresponding
* parameters to be bound to the SQL statement (the second array element).
*/
public function build($query)
{
$parts = [];
if ($query->fields !== null) {
$parts['fields'] = (array) $query->fields;
}
if ($query->limit !== null && $query->limit >= 0) {
$parts['size'] = $query->limit;
}
if ($query->offset > 0) {
$parts['from'] = (int) $query->offset;
}
if (empty($parts['query'])) {
$parts['query'] = ["match_all" => (object)[]];
}
$whereFilter = $this->buildCondition($query->where);
if (is_string($query->filter)) {
if (empty($whereFilter)) {
$parts['filter'] = $query->filter;
} else {
$parts['filter'] = '{"and": [' . $query->filter . ', ' . Json::encode($whereFilter) . ']}';
}
} elseif ($query->filter !== null) {
if (empty($whereFilter)) {
$parts['filter'] = $query->filter;
} else {
$parts['filter'] = ['and' => [$query->filter, $whereFilter]];
}
} elseif (!empty($whereFilter)) {
$parts['filter'] = $whereFilter;
}
$sort = $this->buildOrderBy($query->orderBy);
if (!empty($sort)) {
$parts['sort'] = $sort;
}
if (!empty($query->facets)) {
$parts['facets'] = $query->facets;
}
$options = [];
if ($query->timeout !== null) {
$options['timeout'] = $query->timeout;
}
return [
'queryParts' => $parts,
'index' => $query->index,
'type' => $query->type,
'options' => $options,
];
}
/**
* adds order by condition to the query
*/
public function buildOrderBy($columns)
{
if (empty($columns)) {
return [];
}
$orders = [];
foreach ($columns as $name => $direction) {
if (is_string($direction)) {
$column = $direction;
$direction = SORT_ASC;
} else {
$column = $name;
}
if ($column == ActiveRecord::PRIMARY_KEY_NAME) {
$column = '_uid';
}
// allow elasticsearch extended syntax as described in http://www.elasticsearch.org/guide/reference/api/search/sort/
if (is_array($direction)) {
$orders[] = [$column => $direction];
} else {
$orders[] = [$column => ($direction === SORT_DESC ? 'desc' : 'asc')];
}
}
return $orders;
}
/**
* Parses the condition specification and generates the corresponding SQL expression.
* @param string|array $condition the condition specification. Please refer to [[Query::where()]]
* on how to specify a condition.
* @param array $params the binding parameters to be populated
* @return string the generated SQL expression
* @throws \yii\db\Exception if the condition is in bad format
*/
public function buildCondition($condition)
{
static $builders = array(
'and' => 'buildAndCondition',
'or' => 'buildAndCondition',
'between' => 'buildBetweenCondition',
'not between' => 'buildBetweenCondition',
'in' => 'buildInCondition',
'not in' => 'buildInCondition',
'like' => 'buildLikeCondition',
'not like' => 'buildLikeCondition',
'or like' => 'buildLikeCondition',
'or not like' => 'buildLikeCondition',
);
if (empty($condition)) {
return [];
}
if (!is_array($condition)) {
throw new NotSupportedException('String conditions in where() are not supported by elasticsearch.');
}
if (isset($condition[0])) { // operator format: operator, operand 1, operand 2, ...
$operator = strtolower($condition[0]);
if (isset($builders[$operator])) {
$method = $builders[$operator];
array_shift($condition);
return $this->$method($operator, $condition);
} else {
throw new InvalidParamException('Found unknown operator in query: ' . $operator);
}
} else { // hash format: 'column1' => 'value1', 'column2' => 'value2', ...
return $this->buildHashCondition($condition);
}
}
private function buildHashCondition($condition)
{
$parts = [];
foreach($condition as $attribute => $value) {
if ($attribute == ActiveRecord::PRIMARY_KEY_NAME) {
if ($value == null) { // there is no null pk
$parts[] = ['script' => ['script' => '0==1']];
} else {
$parts[] = ['ids' => ['values' => is_array($value) ? $value : [$value]]];
}
} else {
if (is_array($value)) { // IN condition
$parts[] = ['in' => [$attribute => $value]];
} else {
if ($value === null) {
$parts[] = ['missing' => ['field' => $attribute, 'existence' => true, 'null_value' => true]];
} else {
$parts[] = ['term' => [$attribute => $value]];
}
}
}
}
return count($parts) === 1 ? $parts[0] : ['and' => $parts];
}
private function buildAndCondition($operator, $operands)
{
$parts = [];
foreach ($operands as $operand) {
if (is_array($operand)) {
$operand = $this->buildCondition($operand);
}
if (!empty($operand)) {
$parts[] = $operand;
}
}
if (!empty($parts)) {
return [$operator => $parts];
} else {
return [];
}
}
private function buildBetweenCondition($operator, $operands)
{
if (!isset($operands[0], $operands[1], $operands[2])) {
throw new InvalidParamException("Operator '$operator' requires three operands.");
}
list($column, $value1, $value2) = $operands;
if ($column == ActiveRecord::PRIMARY_KEY_NAME) {
throw new NotSupportedException('Between condition is not supported for primaryKey.');
}
$filter = ['range' => [$column => ['gte' => $value1, 'lte' => $value2]]];
if ($operator == 'not between') {
$filter = ['not' => $filter];
}
return $filter;
}
private function buildInCondition($operator, $operands)
{
if (!isset($operands[0], $operands[1])) {
throw new InvalidParamException("Operator '$operator' requires two operands.");
}
list($column, $values) = $operands;
$values = (array)$values;
if (empty($values) || $column === []) {
return $operator === 'in' ? ['script' => ['script' => '0==1']] : [];
}
if (count($column) > 1) {
return $this->buildCompositeInCondition($operator, $column, $values, $params);
} elseif (is_array($column)) {
$column = reset($column);
}
$canBeNull = false;
foreach ($values as $i => $value) {
if (is_array($value)) {
$values[$i] = $value = isset($value[$column]) ? $value[$column] : null;
}
if ($value === null) {
$canBeNull = true;
unset($values[$i]);
}
}
if ($column == ActiveRecord::PRIMARY_KEY_NAME) {
if (empty($values) && $canBeNull) { // there is no null pk
$filter = ['script' => ['script' => '0==1']];
} else {
$filter = ['ids' => ['values' => array_values($values)]];
if ($canBeNull) {
$filter = ['or' => [$filter, ['missing' => ['field' => $column, 'existence' => true, 'null_value' => true]]]];
}
}
} else {
if (empty($values) && $canBeNull) {
$filter = ['missing' => ['field' => $column, 'existence' => true, 'null_value' => true]];
} else {
$filter = ['in' => [$column => array_values($values)]];
if ($canBeNull) {
$filter = ['or' => [$filter, ['missing' => ['field' => $column, 'existence' => true, 'null_value' => true]]]];
}
}
}
if ($operator == 'not in') {
$filter = ['not' => $filter];
}
return $filter;
}
protected function buildCompositeInCondition($operator, $columns, $values)
{
throw new NotSupportedException('composite in is not supported by elasticsearch.');
$vss = array();
foreach ($values as $value) {
$vs = array();
foreach ($columns as $column) {
if (isset($value[$column])) {
$phName = self::PARAM_PREFIX . count($params);
$params[$phName] = $value[$column];
$vs[] = $phName;
} else {
$vs[] = 'NULL';
}
}
$vss[] = '(' . implode(', ', $vs) . ')';
}
foreach ($columns as $i => $column) {
if (strpos($column, '(') === false) {
$columns[$i] = $this->db->quoteColumnName($column);
}
}
return '(' . implode(', ', $columns) . ") $operator (" . implode(', ', $vss) . ')';
}
private function buildLikeCondition($operator, $operands)
{
throw new NotSupportedException('like conditions is not supported by elasticsearch.');
if (!isset($operands[0], $operands[1])) {
throw new Exception("Operator '$operator' requires two operands.");
}
list($column, $values) = $operands;
$values = (array)$values;
if (empty($values)) {
return $operator === 'LIKE' || $operator === 'OR LIKE' ? '0==1' : '';
}
if ($operator === 'LIKE' || $operator === 'NOT LIKE') {
$andor = ' AND ';
} else {
$andor = ' OR ';
$operator = $operator === 'OR LIKE' ? 'LIKE' : 'NOT LIKE';
}
if (strpos($column, '(') === false) {
$column = $this->db->quoteColumnName($column);
}
$parts = array();
foreach ($values as $value) {
$phName = self::PARAM_PREFIX . count($params);
$params[$phName] = $value;
$parts[] = "$column $operator $phName";
}
return implode($andor, $parts);
}
}
Elasticsearch Query and ActiveRecord for Yii 2
==============================================
This extension provides the [elasticsearch](http://www.elasticsearch.org/) integration for the Yii2 framework.
It includes basic querying/search support and also implements the `ActiveRecord` pattern that allows you to store active
records in elasticsearch.
To use this extension, you have to configure the Connection class in your application configuration:
```php
return [
//....
'components' => [
'elasticsearch' => [
'class' => 'yii\elasticsearch\Connection',
'nodes' => [
['http_address' => '127.0.0.1:9200'],
// configure more hosts if you have a cluster
],
],
]
];
```
Installation
------------
The preferred way to install this extension is through [composer](http://getcomposer.org/download/).
Either run
```
php composer.phar require yiisoft/yii2-elasticsearch "*"
```
or add
```json
"yiisoft/yii2-elasticsearch": "*"
```
to the require section of your composer.json.
Using the Query
---------------
TBD
Using the ActiveRecord
----------------------
For general information on how to use yii's ActiveRecord please refer to the [guide](https://github.com/yiisoft/yii2/blob/master/docs/guide/active-record.md).
For defining an elasticsearch ActiveRecord class your record class needs to extend from `yii\elasticsearch\ActiveRecord` and
implement at least the `attributes()` method to define the attributes of the record.
The primary key (the `_id` field in elasticsearch terms) is represented by `getId()` and `setId()` and can not be changed.
The primary key is not part of the attributes.
primary key can be defined via [[primaryKey()]] which defaults to `id` if not specified.
The primaryKey needs to be part of the attributes so make sure you have an `id` attribute defined if you do
not specify your own primary key.
The following is an example model called `Customer`:
```php
class Customer extends \yii\elasticsearch\ActiveRecord
{
/**
* @return array the list of attributes for this record
*/
public function attributes()
{
return ['id', 'name', 'address', 'registration_date'];
}
/**
* @return ActiveRelation defines a relation to the Order record (can be in other database, e.g. redis or sql)
*/
public function getOrders()
{
return $this->hasMany(Order::className(), ['customer_id' => 'id'])->orderBy('id');
}
/**
* Defines a scope that modifies the `$query` to return only active(status = 1) customers
*/
public static function active($query)
{
$query->andWhere(array('status' => 1));
}
}
```
You may override [[index()]] and [[type()]] to define the index and type this record represents.
The general usage of elasticsearch ActiveRecord is very similar to the database ActiveRecord as described in the
[guide](https://github.com/yiisoft/yii2/blob/master/docs/guide/active-record.md).
It supports the same interface and features except the following limitations and additions(*!*):
- As elasticsearch does not support SQL, the query API does not support `join()`, `groupBy()`, `having()` and `union()`.
Sorting, limit, offset and conditional where are all supported.
- `from()` does not select the tables, but the [index](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/glossary.html#glossary-index)
and [type](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/glossary.html#glossary-type) to query against.
- `select()` has been replaced with `fields()` which basically does the same but `fields` is more elasticsearch terminology.
It defines the fields to retrieve from a document.
- `via`-relations can not be defined via a table as there are not tables in elasticsearch. You can only define relations via other records.
- As elasticsearch is a data storage and search engine there is of course support added for search your records.
There are `query()`, `filter()` and `addFacets()` methods that allows to compose an elasticsearch query.
See the usage example below on how they work and check out the [Query DSL](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl.html)
on how to compose `query` and `filter` parts.
- It is also possible to define relations from elasticsearch ActiveRecords to normal ActiveRecord classes and vice versa.
Elasticsearch separates primary key from attributes. You need to set the `id` property of the record to set its primary key.
Usage example:
```php
$customer = new Customer();
$customer->id = 1;
$customer->attributes = ['name' => 'test'];
$customer->save();
$customer = Customer::get(1); // get a record by pk
$customers = Customer::get([1,2,3]); // get a records multiple by pk
$customer = Customer::find()->where(['name' => 'test'])->one(); // find by query
$customers = Customer::find()->active()->all(); // find all by query (using the `active` scope)
// http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-field-query.html
$result = Article::find()->query(["field" => ["title" => "yii"]])->all(); // articles whose title contains "yii"
// http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-flt-query.html
$query = Article::find()->query([
"fuzzy_like_this" => [
"fields" => ["title", "description"],
"like_text" => "This query will return articles that are similar to this text :-)",
"max_query_terms" : 12
]
]);
$query->all(); // gives you all the documents
// you can add facets to your search:
$query->addStatisticalFacet('click_stats', ['field' => 'visit_count']);
$query->search(); // gives you all the records + stats about the visit_count field. e.g. mean, sum, min, max etc...
```
And there is so much more in it. "it’s endless what you can build"[¹](http://www.elasticsearch.org/)
{
"name": "yiisoft/yii2-elasticsearch",
"description": "Elasticsearch integration and ActiveRecord for the Yii framework",
"keywords": ["yii", "elasticsearch", "active-record", "search", "fulltext"],
"type": "yii2-extension",
"license": "BSD-3-Clause",
"support": {
"issues": "https://github.com/yiisoft/yii2/issues?labels=ext%3Aelasticsearch",
"forum": "http://www.yiiframework.com/forum/",
"wiki": "http://www.yiiframework.com/wiki/",
"irc": "irc://irc.freenode.net/yii",
"source": "https://github.com/yiisoft/yii2"
},
"authors": [
{
"name": "Carsten Brandt",
"email": "mail@cebe.cc"
}
],
"require": {
"yiisoft/yii2": "*",
"ext-curl": "*"
},
"autoload": {
"psr-0": { "yii\\elasticsearch\\": "" }
},
"target-dir": "yii/elasticsearch"
}
......@@ -63,7 +63,7 @@ abstract class Generator extends Model
abstract public function generate();
/**
* @inheritdoc
* {@inheritdoc}
*/
public function init()
{
......@@ -164,7 +164,7 @@ abstract class Generator extends Model
}
/**
* @inheritdoc
* {@inheritdoc}
*
* Child classes should override this method like the following so that the parent
* rules are included:
......
......@@ -18,25 +18,25 @@ use yii\web\AssetBundle;
class GiiAsset extends AssetBundle
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public $sourcePath = '@yii/gii/assets';
/**
* @inheritdoc
* {@inheritdoc}
*/
public $css = [
'main.css',
'typeahead.js-bootstrap.css',
];
/**
* @inheritdoc
* {@inheritdoc}
*/
public $js = [
'gii.js',
'typeahead.js',
];
/**
* @inheritdoc
* {@inheritdoc}
*/
public $depends = [
'yii\web\YiiAsset',
......
......@@ -54,7 +54,7 @@ use yii\web\HttpException;
class Module extends \yii\base\Module
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public $controllerNamespace = 'yii\gii\controllers';
/**
......@@ -92,7 +92,7 @@ class Module extends \yii\base\Module
/**
* @inheritdoc
* {@inheritdoc}
*/
public function init()
{
......@@ -103,7 +103,7 @@ class Module extends \yii\base\Module
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function beforeAction($action)
{
......
......@@ -5,7 +5,7 @@
"type": "yii2-extension",
"license": "BSD-3-Clause",
"support": {
"issues": "https://github.com/yiisoft/yii2/issues?state=open",
"issues": "https://github.com/yiisoft/yii2/issues?labels=ext%3Agii",
"forum": "http://www.yiiframework.com/forum/",
"wiki": "http://www.yiiframework.com/wiki/",
"irc": "irc://irc.freenode.net/yii",
......
......@@ -38,7 +38,7 @@ class Generator extends \yii\gii\Generator
public $actions = 'index';
/**
* @inheritdoc
* {@inheritdoc}
*/
public function init()
{
......@@ -47,7 +47,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function getName()
{
......@@ -55,7 +55,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function getDescription()
{
......@@ -64,7 +64,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function rules()
{
......@@ -79,7 +79,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function attributeLabels()
{
......@@ -92,7 +92,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function requiredTemplates()
{
......@@ -103,7 +103,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function stickyAttributes()
{
......@@ -111,7 +111,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function hints()
{
......@@ -134,7 +134,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function successMessage()
{
......@@ -149,7 +149,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function generate()
{
......
......@@ -69,7 +69,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function hints()
{
......@@ -95,7 +95,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function stickyAttributes()
{
......@@ -123,7 +123,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function generate()
{
......
......@@ -40,7 +40,7 @@ class <?= $searchModelClass ?> extends Model
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function attributeLabels()
{
......
......@@ -26,7 +26,7 @@ class Generator extends \yii\gii\Generator
/**
* @inheritdoc
* {@inheritdoc}
*/
public function getName()
{
......@@ -34,7 +34,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function getDescription()
{
......@@ -42,7 +42,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function generate()
{
......@@ -55,7 +55,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function rules()
{
......@@ -72,7 +72,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function attributeLabels()
{
......@@ -85,7 +85,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function requiredTemplates()
{
......@@ -93,7 +93,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function stickyAttributes()
{
......@@ -101,7 +101,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function hints()
{
......@@ -114,7 +114,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function successMessage()
{
......
......@@ -32,7 +32,7 @@ class Generator extends \yii\gii\Generator
/**
* @inheritdoc
* {@inheritdoc}
*/
public function getName()
{
......@@ -40,7 +40,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function getDescription()
{
......@@ -48,7 +48,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function rules()
{
......@@ -68,7 +68,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function attributeLabels()
{
......@@ -84,7 +84,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function hints()
{
......@@ -111,7 +111,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function autoCompleteData()
{
......@@ -128,7 +128,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function requiredTemplates()
{
......@@ -136,7 +136,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function stickyAttributes()
{
......@@ -144,7 +144,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function generate()
{
......
......@@ -33,7 +33,7 @@ namespace <?= $generator->ns ?>;
class <?= $className ?> extends <?= '\\' . ltrim($generator->baseClass, '\\') . "\n" ?>
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public static function tableName()
{
......@@ -41,7 +41,7 @@ class <?= $className ?> extends <?= '\\' . ltrim($generator->baseClass, '\\') .
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function rules()
{
......@@ -49,7 +49,7 @@ class <?= $className ?> extends <?= '\\' . ltrim($generator->baseClass, '\\') .
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function attributeLabels()
{
......
......@@ -24,7 +24,7 @@ class Generator extends \yii\gii\Generator
public $moduleID;
/**
* @inheritdoc
* {@inheritdoc}
*/
public function getName()
{
......@@ -32,7 +32,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function getDescription()
{
......@@ -40,7 +40,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function rules()
{
......@@ -54,7 +54,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function attributeLabels()
{
......@@ -65,7 +65,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function hints()
{
......@@ -76,7 +76,7 @@ class Generator extends \yii\gii\Generator
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function successMessage()
{
......@@ -104,7 +104,7 @@ EOD;
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function requiredTemplates()
{
......@@ -112,7 +112,7 @@ EOD;
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function generate()
{
......
......@@ -9,16 +9,16 @@ use yii\helpers\Html;
*/
$asset = yii\gii\GiiAsset::register($this);
?>
<?php $this->beginPage(); ?>
<?php $this->beginPage() ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title><?= Html::encode($this->title) ?></title>
<?php $this->head(); ?>
<?php $this->head() ?>
</head>
<body>
<?php $this->beginBody(); ?>
<?php $this->beginBody() ?>
<?php
NavBar::begin([
'brandLabel' => Html::img($asset->baseUrl . '/logo.png'),
......@@ -47,7 +47,7 @@ NavBar::end();
</div>
</footer>
<?php $this->endBody(); ?>
<?php $this->endBody() ?>
</body>
</html>
<?php $this->endPage(); ?>
<?php $this->endPage() ?>
......@@ -5,7 +5,7 @@
"type": "yii2-extension",
"license": "BSD-3-Clause",
"support": {
"issues": "https://github.com/yiisoft/yii2/issues?state=open",
"issues": "https://github.com/yiisoft/yii2/issues?labels=ext%3Ajui",
"forum": "http://www.yiiframework.com/forum/",
"wiki": "http://www.yiiframework.com/wiki/",
"irc": "irc://irc.freenode.net/yii",
......
......@@ -226,7 +226,7 @@ class ActiveQuery extends \yii\base\Component implements ActiveQueryInterface
{
$record = $this->one($db);
if ($record !== null) {
return $record->$attribute;
return $record->hasAttribute($attribute) ? $record->$attribute : null;
} else {
return null;
}
......
......@@ -48,7 +48,7 @@ class ActiveRecord extends \yii\db\ActiveRecord
}
/**
* @inheritDoc
* {@inheritdoc}
*/
public static function createQuery()
{
......@@ -56,7 +56,7 @@ class ActiveRecord extends \yii\db\ActiveRecord
}
/**
* @inheritDoc
* {@inheritdoc}
*/
public static function createActiveRelation($config = [])
{
......@@ -81,13 +81,13 @@ class ActiveRecord extends \yii\db\ActiveRecord
* This method must be overridden by child classes to define available attributes.
* @return array list of attribute names.
*/
public static function attributes()
public function attributes()
{
throw new InvalidConfigException('The attributes() method of redis ActiveRecord has to be implemented by child classes.');
}
/**
* @inheritDocs
* {@inheritdoc}
*/
public function insert($runValidation = true, $attributes = null)
{
......@@ -294,19 +294,19 @@ class ActiveRecord extends \yii\db\ActiveRecord
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public static function getTableSchema()
{
throw new NotSupportedException('getTableSchema() is not supported by redis ActiveRecord');
throw new NotSupportedException('getTableSchema() is not supported by redis ActiveRecord.');
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public static function findBySql($sql, $params = [])
{
throw new NotSupportedException('findBySql() is not supported by redis ActiveRecord');
throw new NotSupportedException('findBySql() is not supported by redis ActiveRecord.');
}
/**
......
......@@ -30,18 +30,28 @@ use yii\base\InvalidConfigException;
* 'components' => [
* 'cache' => [
* 'class' => 'yii\redis\Cache',
* 'redis' => [
* 'hostname' => 'localhost',
* 'port' => 6379,
* 'database' => 0,
* ]
* ],
* 'redis' => [
* 'class' => 'yii\redis\Connection',
* 'hostname' => 'localhost',
* 'port' => 6379,
* 'database' => 0,
* ]
* ],
* ]
* ~~~
*
* @property Connection $connection The redis connection object. This property is read-only.
* Or if you have configured the redis [[Connection]] as an application component, the following is sufficient:
*
* ~~~
* [
* 'components' => [
* 'cache' => [
* 'class' => 'yii\redis\Cache',
* // 'redis' => 'redis' // id of the connection application component
* ],
* ],
* ]
* ~~~
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
......@@ -49,7 +59,9 @@ use yii\base\InvalidConfigException;
class Cache extends \yii\caching\Cache
{
/**
* @var Connection|string the Redis [[Connection]] object or the application component ID of the Redis [[Connection]].
* @var Connection|string|array the Redis [[Connection]] object or the application component ID of the Redis [[Connection]].
* This can also be an array that is used to create a redis [[Connection]] instance in case you do not want do configure
* redis connection as an application component.
* After the Cache object is created, if you want to change this property, you should only assign it
* with a Redis [[Connection]] object.
*/
......@@ -57,15 +69,20 @@ class Cache extends \yii\caching\Cache
/**
* Initializes the DbCache component.
* This method will initialize the [[db]] property to make sure it refers to a valid DB connection.
* @throws InvalidConfigException if [[db]] is invalid.
* Initializes the redis Cache component.
* This method will initialize the [[redis]] property to make sure it refers to a valid redis connection.
* @throws InvalidConfigException if [[redis]] is invalid.
*/
public function init()
{
parent::init();
if (is_string($this->redis)) {
$this->redis = Yii::$app->getComponent($this->redis);
} else if (is_array($this->redis)) {
if (!isset($this->redis['class'])) {
$this->redis['class'] = Connection::className();
}
$this->redis = Yii::createObject($this->redis);
}
if (!$this->redis instanceof Connection) {
throw new InvalidConfigException("Cache::redis must be either a Redis connection instance or the application component ID of a Redis connection.");
......@@ -88,7 +105,7 @@ class Cache extends \yii\caching\Cache
}
/**
* @inheritDocs
* {@inheritdoc}
*/
protected function getValue($key)
{
......@@ -96,7 +113,7 @@ class Cache extends \yii\caching\Cache
}
/**
* @inheritDocs
* {@inheritdoc}
*/
protected function getValues($keys)
{
......@@ -110,7 +127,7 @@ class Cache extends \yii\caching\Cache
}
/**
* @inheritDocs
* {@inheritdoc}
*/
protected function setValue($key, $value, $expire)
{
......@@ -123,7 +140,7 @@ class Cache extends \yii\caching\Cache
}
/**
* @inheritDocs
* {@inheritdoc}
*/
protected function setValues($data, $expire)
{
......@@ -157,7 +174,7 @@ class Cache extends \yii\caching\Cache
}
/**
* @inheritDocs
* {@inheritdoc}
*/
protected function addValue($key, $value, $expire)
{
......@@ -170,7 +187,7 @@ class Cache extends \yii\caching\Cache
}
/**
* @inheritDocs
* {@inheritdoc}
*/
protected function deleteValue($key)
{
......@@ -178,7 +195,7 @@ class Cache extends \yii\caching\Cache
}
/**
* @inheritDocs
* {@inheritdoc}
*/
protected function flushValues()
{
......
Redis Cache and ActiveRecord for Yii 2
======================================
Redis Cache, Session and ActiveRecord for Yii 2
===============================================
This extension provides the [redis](http://redis.io/) key-value store support for the Yii2 framework.
It includes a `Cache` class and implents the `ActiveRecord` pattern that allows you to store active
records in redis.
It includes a `Cache` and `Session` storage handler and implents the `ActiveRecord` pattern that allows
you to store active records in redis.
To use this extension, you have to configure the Connection class in your application configuration:
......@@ -21,7 +21,31 @@ return [
];
```
To use the `Cache` component, you also have to configure the `cache` component to be `yii\redis\Cache`:
Installation
------------
The preferred way to install this extension is through [composer](http://getcomposer.org/download/).
Either run
```
php composer.phar require yiisoft/yii2-redis "*"
```
or add
```json
"yiisoft/yii2-redis": "*"
```
to the require section of your composer.json.
Using the Cache component
-------------------------
To use the `Cache` component, in addtition to configuring the connection as described above,
you also have to configure the `cache` component to be `yii\redis\Cache`:
```php
return [
......@@ -35,26 +59,64 @@ return [
];
```
If you only use the redis cache, you can also configure the parameters of the connection within the
cache component (no connection application component needs to be configured in this case):
Installation
------------
```php
return [
//....
'components' => [
// ...
'cache' => [
'class' => 'yii\redis\Cache',
'redis' => [
'hostname' => 'localhost',
'port' => 6379,
'database' => 0,
],
],
]
];
```
The preferred way to install this extension is through [composer](http://getcomposer.org/download/).
Using the Session component
---------------------------
Either run
To use the `Session` component, in addtition to configuring the connection as described above,
you also have to configure the `session` component to be `yii\redis\Session`:
```
php composer.phar require yiisoft/yii2-redis "*"
```php
return [
//....
'components' => [
// ...
'session' => [
'class' => 'yii\redis\Session',
],
]
];
```
or add
If you only use the redis session, you can also configure the parameters of the connection within the
cache component (no connection application component needs to be configured in this case):
```json
"yiisoft/yii2-redis": "*"
```php
return [
//....
'components' => [
// ...
'session' => [
'class' => 'yii\redis\Session',
'redis' => [
'hostname' => 'localhost',
'port' => 6379,
'database' => 0,
],
],
]
];
```
to the require section of your composer.json.
Using the redis ActiveRecord
----------------------------
......@@ -72,10 +134,29 @@ The following is an example model called `Customer`:
```php
class Customer extends \yii\redis\ActiveRecord
{
public function attributes()
{
return ['id', 'name', 'address', 'registration_date'];
}
/**
* @return array the list of attributes for this record
*/
public function attributes()
{
return ['id', 'name', 'address', 'registration_date'];
}
/**
* @return ActiveRelation defines a relation to the Order record (can be in other database, e.g. elasticsearch or sql)
*/
public function getOrders()
{
return $this->hasMany(Order::className(), ['customer_id' => 'id']);
}
/**
* Defines a scope that modifies the `$query` to return only active(status = 1) customers
*/
public static function active($query)
{
$query->andWhere(array('status' => 1));
}
}
```
......@@ -88,4 +169,16 @@ It supports the same interface and features except the following limitations:
(orderBy() is not yet implemented: [#1305](https://github.com/yiisoft/yii2/issues/1305))
- `via`-relations can not be defined via a table as there are not tables in redis. You can only define relations via other records.
It is also possible to define relations from redis ActiveRecords to normal ActiveRecord classes and vice versa.
\ No newline at end of file
It is also possible to define relations from redis ActiveRecords to normal ActiveRecord classes and vice versa.
Usage example:
```php
$customer = new Customer();
$customer->attributes = ['name' => 'test'];
$customer->save();
echo $customer->id; // id will automatically be incremented if not set explicitly
$customer = Customer::find()->where(['name' => 'test'])->one(); // find by query
$customer = Customer::find()->active()->all(); // find all by query (using the `active` scope)
```
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\redis;
use Yii;
use yii\base\InvalidConfigException;
/**
* Redis Session implements a session component using [redis](http://redis.io/) as the storage medium.
*
* Redis Session requires redis version 2.6.12 or higher to work properly.
*
* It needs to be configured with a redis [[Connection]] that is also configured as an application component.
* By default it will use the `redis` application component.
*
* To use redis Session as the session application component, configure the application as follows,
*
* ~~~
* [
* 'components' => [
* 'session' => [
* 'class' => 'yii\redis\Session',
* 'redis' => [
* 'hostname' => 'localhost',
* 'port' => 6379,
* 'database' => 0,
* ]
* ],
* ],
* ]
* ~~~
*
* Or if you have configured the redis [[Connection]] as an application component, the following is sufficient:
*
* ~~~
* [
* 'components' => [
* 'session' => [
* 'class' => 'yii\redis\Session',
* // 'redis' => 'redis' // id of the connection application component
* ],
* ],
* ]
* ~~~
*
* @property boolean $useCustomStorage Whether to use custom storage. This property is read-only.
*
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
*/
class Session extends \yii\web\Session
{
/**
* @var Connection|string|array the Redis [[Connection]] object or the application component ID of the Redis [[Connection]].
* This can also be an array that is used to create a redis [[Connection]] instance in case you do not want do configure
* redis connection as an application component.
* After the Session object is created, if you want to change this property, you should only assign it
* with a Redis [[Connection]] object.
*/
public $redis = 'redis';
/**
* @var string a string prefixed to every cache key so that it is unique. If not set,
* it will use a prefix generated from [[Application::id]]. You may set this property to be an empty string
* if you don't want to use key prefix. It is recommended that you explicitly set this property to some
* static value if the cached data needs to be shared among multiple applications.
*
* To ensure interoperability, only alphanumeric characters should be used.
*/
public $keyPrefix;
/**
* Initializes the redis Session component.
* This method will initialize the [[redis]] property to make sure it refers to a valid redis connection.
* @throws InvalidConfigException if [[redis]] is invalid.
*/
public function init()
{
if (is_string($this->redis)) {
$this->redis = Yii::$app->getComponent($this->redis);
} else if (is_array($this->redis)) {
if (!isset($this->redis['class'])) {
$this->redis['class'] = Connection::className();
}
$this->redis = Yii::createObject($this->redis);
}
if (!$this->redis instanceof Connection) {
throw new InvalidConfigException("Session::redis must be either a Redis connection instance or the application component ID of a Redis connection.");
}
if ($this->keyPrefix === null) {
$this->keyPrefix = substr(md5(Yii::$app->id), 0, 5);
} elseif (!ctype_alnum($this->keyPrefix)) {
throw new InvalidConfigException(get_class($this) . '::keyPrefix should only contain alphanumeric characters.');
}
parent::init();
}
/**
* Returns a value indicating whether to use custom session storage.
* This method overrides the parent implementation and always returns true.
* @return boolean whether to use custom storage.
*/
public function getUseCustomStorage()
{
return true;
}
/**
* Session read handler.
* Do not call this method directly.
* @param string $id session ID
* @return string the session data
*/
public function readSession($id)
{
$data = $this->redis->executeCommand('GET', [$this->calculateKey($id)]);
return $data === false ? '' : $data;
}
/**
* Session write handler.
* Do not call this method directly.
* @param string $id session ID
* @param string $data session data
* @return boolean whether session write is successful
*/
public function writeSession($id, $data)
{
return (bool) $this->redis->executeCommand('SET', [$this->calculateKey($id), $data, 'EX', $this->getTimeout()]);
}
/**
* Session destroy handler.
* Do not call this method directly.
* @param string $id session ID
* @return boolean whether session is destroyed successfully
*/
public function destroySession($id)
{
return (bool) $this->redis->executeCommand('DEL', [$this->calculateKey($id)]);
}
/**
* Generates a unique key used for storing session data in cache.
* @param string $id session variable name
* @return string a safe cache key associated with the session variable name
*/
protected function calculateKey($id)
{
return $this->keyPrefix . md5(json_encode([__CLASS__, $id]));
}
}
{
"name": "yiisoft/yii2-redis",
"description": "Redis Cache and ActiveRecord for the Yii framework",
"keywords": ["yii", "redis", "active-record", "cache"],
"description": "Redis Cache, Session and ActiveRecord for the Yii framework",
"keywords": ["yii", "redis", "active-record", "cache", "session"],
"type": "yii2-extension",
"license": "BSD-3-Clause",
"support": {
......
......@@ -5,7 +5,7 @@
"type": "yii2-extension",
"license": "BSD-3-Clause",
"support": {
"issues": "https://github.com/yiisoft/yii2/issues?state=open",
"issues": "https://github.com/yiisoft/yii2/issues?labels=ext%3Asmarty",
"forum": "http://www.yiiframework.com/forum/",
"wiki": "http://www.yiiframework.com/wiki/",
"irc": "irc://irc.freenode.net/yii",
......
......@@ -176,7 +176,7 @@ class ActiveQuery extends Query implements ActiveQueryInterface
}
/**
* @inheritdoc
* {@inheritdoc}
*/
protected function defaultConnection()
{
......
......@@ -39,14 +39,17 @@ use yii\base\NotSupportedException;
*
* To build SELECT SQL statements, please use [[Query]] and [[QueryBuilder]] instead.
*
* @property \yii\sphinx\Connection $db the Sphinx connection that this command is associated with.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class Command extends \yii\db\Command
{
/**
* @var \yii\sphinx\Connection the Sphinx connection that this command is associated with.
*/
public $db;
/**
* Creates a batch INSERT command.
* For example,
*
......@@ -194,7 +197,7 @@ class Command extends \yii\db\Command
// Not Supported :
/**
* @inheritdoc
* {@inheritdoc}
*/
public function createTable($table, $columns, $options = null)
{
......@@ -202,7 +205,7 @@ class Command extends \yii\db\Command
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function renameTable($table, $newName)
{
......@@ -210,7 +213,7 @@ class Command extends \yii\db\Command
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function dropTable($table)
{
......@@ -218,7 +221,7 @@ class Command extends \yii\db\Command
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function truncateTable($table)
{
......@@ -226,7 +229,7 @@ class Command extends \yii\db\Command
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function addColumn($table, $column, $type)
{
......@@ -234,7 +237,7 @@ class Command extends \yii\db\Command
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function dropColumn($table, $column)
{
......@@ -242,7 +245,7 @@ class Command extends \yii\db\Command
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function renameColumn($table, $oldName, $newName)
{
......@@ -250,7 +253,7 @@ class Command extends \yii\db\Command
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function alterColumn($table, $column, $type)
{
......@@ -258,7 +261,7 @@ class Command extends \yii\db\Command
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function addPrimaryKey($name, $table, $columns)
{
......@@ -266,7 +269,7 @@ class Command extends \yii\db\Command
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function dropPrimaryKey($name, $table)
{
......@@ -274,7 +277,7 @@ class Command extends \yii\db\Command
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete = null, $update = null)
{
......@@ -282,7 +285,7 @@ class Command extends \yii\db\Command
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function dropForeignKey($name, $table)
{
......@@ -290,7 +293,7 @@ class Command extends \yii\db\Command
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function createIndex($name, $table, $columns, $unique = false)
{
......@@ -298,7 +301,7 @@ class Command extends \yii\db\Command
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function dropIndex($name, $table)
{
......@@ -306,7 +309,7 @@ class Command extends \yii\db\Command
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function resetSequence($table, $value = null)
{
......@@ -314,7 +317,7 @@ class Command extends \yii\db\Command
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function checkIntegrity($check = true, $schema = '')
{
......
......@@ -47,11 +47,14 @@ use yii\base\NotSupportedException;
*
* Note: while this class extends "yii\db\Connection" some of its methods are not supported.
*
* @method \yii\sphinx\Schema getSchema() The schema information for this Sphinx connection
* @method \yii\sphinx\QueryBuilder getQueryBuilder() the query builder for this Sphinx connection
*
* @property string $lastInsertID The row ID of the last row inserted, or the last value retrieved from the
* sequence object. This property is read-only.
* @property Schema $schema The schema information for this Sphinx connection. This property is read-only.
* @property \yii\sphinx\QueryBuilder $queryBuilder The query builder for this Sphinx connection. This property is
* read-only.
* @method \yii\sphinx\Schema getSchema() The schema information for this Sphinx connection
* @method \yii\sphinx\QueryBuilder getQueryBuilder() the query builder for this Sphinx connection
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
......@@ -59,7 +62,7 @@ use yii\base\NotSupportedException;
class Connection extends \yii\db\Connection
{
/**
* @inheritdoc
* {@inheritdoc}
*/
public $schemaMap = [
'mysqli' => 'yii\sphinx\Schema', // MySQL
......
......@@ -41,6 +41,8 @@ use yii\db\QueryTrait;
*
* Warning: even if you do not set any query limit, implicit LIMIT 0,20 is present by default!
*
* @property Connection $connection Sphinx connection instance.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
......
......@@ -7,6 +7,7 @@
namespace yii\sphinx;
use yii\base\InvalidParamException;
use yii\base\Object;
use yii\db\Exception;
use yii\db\Expression;
......@@ -760,12 +761,12 @@ class QueryBuilder extends Object
* operator is `LIKE` or `OR LIKE` and empty if operator is `NOT LIKE` or `OR NOT LIKE`.
* @param array $params the binding parameters to be populated
* @return string the generated SQL expression
* @throws Exception if wrong number of operands have been given.
* @throws InvalidParamException if wrong number of operands have been given.
*/
public function buildLikeCondition($indexes, $operator, $operands, &$params)
{
if (!isset($operands[0], $operands[1])) {
throw new Exception("Operator '$operator' requires two operands.");
throw new InvalidParamException("Operator '$operator' requires two operands.");
}
list($column, $values) = $operands;
......
......@@ -15,12 +15,12 @@ use yii\caching\GroupDependency;
/**
* Schema represents the Sphinx schema information.
*
* @property QueryBuilder $queryBuilder The query builder for this connection. This property is read-only.
* @property string[] $indexNames All index names in the Sphinx. This property is read-only.
* @property string[] $indexTypes ALL index types in the Sphinx (index name => index type).
* This property is read-only.
* @property IndexSchema[] $tableSchemas The metadata for all indexes in the Sphinx. Each array element is an
* @property IndexSchema[] $indexSchemas The metadata for all indexes in the Sphinx. Each array element is an
* instance of [[IndexSchema]] or its child class. This property is read-only.
* @property array $indexTypes All index types in the Sphinx in format: index name => index type. This
* property is read-only.
* @property QueryBuilder $queryBuilder The query builder for this connection. This property is read-only.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
......
......@@ -5,7 +5,7 @@
"type": "yii2-extension",
"license": "BSD-3-Clause",
"support": {
"issues": "https://github.com/yiisoft/yii2/issues?state=open",
"issues": "https://github.com/yiisoft/yii2/issues?labels=ext%3Asphinx",
"forum": "http://www.yiiframework.com/forum/",
"wiki": "http://www.yiiframework.com/wiki/",
"irc": "irc://irc.freenode.net/yii",
......
......@@ -66,6 +66,10 @@ use yii\mail\BaseMailer;
*
* @see http://swiftmailer.org
*
* @property array|\Swift_Mailer $swiftMailer Swift mailer instance or array configuration. This property is
* read-only.
* @property array|\Swift_Transport $transport This property is read-only.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
......@@ -119,7 +123,7 @@ class Mailer extends BaseMailer
}
/**
* @inheritdoc
* {@inheritdoc}
*/
protected function sendMessage($message)
{
......
......@@ -16,7 +16,8 @@ use yii\mail\BaseMessage;
* @see Mailer
*
* @method Mailer getMailer() returns mailer instance.
* @property \Swift_Message $swiftMessage vendor message instance.
*
* @property \Swift_Message $swiftMessage Swift message instance. This property is read-only.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
......@@ -40,7 +41,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function getCharset()
{
......@@ -48,7 +49,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function setCharset($charset)
{
......@@ -57,7 +58,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function getFrom()
{
......@@ -65,7 +66,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function setFrom($from)
{
......@@ -74,7 +75,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function getReplyTo()
{
......@@ -82,7 +83,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function setReplyTo($replyTo)
{
......@@ -91,7 +92,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function getTo()
{
......@@ -99,7 +100,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function setTo($to)
{
......@@ -108,7 +109,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function getCc()
{
......@@ -116,7 +117,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function setCc($cc)
{
......@@ -125,7 +126,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function getBcc()
{
......@@ -133,7 +134,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function setBcc($bcc)
{
......@@ -142,7 +143,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function getSubject()
{
......@@ -150,7 +151,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function setSubject($subject)
{
......@@ -159,7 +160,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function setTextBody($text)
{
......@@ -168,7 +169,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function setHtmlBody($html)
{
......@@ -221,7 +222,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function attach($fileName, array $options = [])
{
......@@ -237,7 +238,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function attachContent($content, array $options = [])
{
......@@ -253,7 +254,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function embed($fileName, array $options = [])
{
......@@ -268,7 +269,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function embedContent($content, array $options = [])
{
......@@ -283,7 +284,7 @@ class Message extends BaseMessage
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function toString()
{
......
......@@ -5,7 +5,7 @@
"type": "yii2-extension",
"license": "BSD-3-Clause",
"support": {
"issues": "https://github.com/yiisoft/yii2/issues?state=open",
"issues": "https://github.com/yiisoft/yii2/issues?labels=ext%3Aswiftmailer",
"forum": "http://www.yiiframework.com/forum/",
"wiki": "http://www.yiiframework.com/wiki/",
"irc": "irc://irc.freenode.net/yii",
......
......@@ -5,7 +5,7 @@
"type": "yii2-extension",
"license": "BSD-3-Clause",
"support": {
"issues": "https://github.com/yiisoft/yii2/issues?state=open",
"issues": "https://github.com/yiisoft/yii2/issues?labels=ext%3Atwig",
"forum": "http://www.yiiframework.com/forum/",
"wiki": "http://www.yiiframework.com/wiki/",
"irc": "irc://irc.freenode.net/yii",
......
......@@ -9,7 +9,7 @@
var el = document.createElement('input'),
name = 'onpaste';
el.setAttribute(name, '');
return (typeof el[name] === 'function')?'paste':'input';
return (typeof el[name] === 'function')?'paste':'input';
}
var pasteEventName = getPasteEvent() + ".mask",
......@@ -322,9 +322,9 @@ $.fn.extend({
.bind("keydown.mask", keydownEvent)
.bind("keypress.mask", keypressEvent)
.bind(pasteEventName, function() {
setTimeout(function() {
setTimeout(function() {
var pos=checkVal(true);
input.caret(pos);
input.caret(pos);
if (settings.completed && pos == input.val().length)
settings.completed.call(input);
}, 0);
......
......@@ -199,7 +199,7 @@ abstract class Application extends Module
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function init()
{
......
......@@ -87,9 +87,9 @@ class Controller extends Component implements ViewContextInterface
*
* ~~~
* return [
* 'action1' => '@app/components/Action1',
* 'action1' => 'app\components\Action1',
* 'action2' => [
* 'class' => '@app/components/Action2',
* 'class' => 'app\components\Action2',
* 'property1' => 'value1',
* 'property2' => 'value2',
* ],
......@@ -305,7 +305,7 @@ class Controller extends Component implements ViewContextInterface
public function render($view, $params = [])
{
$output = $this->getView()->render($view, $params, $this);
$layoutFile = $this->findLayoutFile();
$layoutFile = $this->findLayoutFile($this->getView());
if ($layoutFile !== false) {
return $this->getView()->renderFile($layoutFile, ['content' => $output], $this);
} else {
......@@ -386,38 +386,39 @@ class Controller extends Component implements ViewContextInterface
/**
* Finds the applicable layout file.
* @param View $view the view object to render the layout file
* @return string|boolean the layout file path, or false if layout is not needed.
* Please refer to [[render()]] on how to specify this parameter.
* @throws InvalidParamException if an invalid path alias is used to specify the layout
*/
protected function findLayoutFile()
protected function findLayoutFile($view)
{
$module = $this->module;
if (is_string($this->layout)) {
$view = $this->layout;
$layout = $this->layout;
} elseif ($this->layout === null) {
while ($module !== null && $module->layout === null) {
$module = $module->module;
}
if ($module !== null && is_string($module->layout)) {
$view = $module->layout;
$layout = $module->layout;
}
}
if (!isset($view)) {
if (!isset($layout)) {
return false;
}
if (strncmp($view, '@', 1) === 0) {
$file = Yii::getAlias($view);
} elseif (strncmp($view, '/', 1) === 0) {
$file = Yii::$app->getLayoutPath() . DIRECTORY_SEPARATOR . $view;
if (strncmp($layout, '@', 1) === 0) {
$file = Yii::getAlias($layout);
} elseif (strncmp($layout, '/', 1) === 0) {
$file = Yii::$app->getLayoutPath() . DIRECTORY_SEPARATOR . $layout;
} else {
$file = $module->getLayoutPath() . DIRECTORY_SEPARATOR . $view;
$file = $module->getLayoutPath() . DIRECTORY_SEPARATOR . $layout;
}
if (pathinfo($file, PATHINFO_EXTENSION) === '') {
$file .= '.php';
$file .= $view->defaultExtension;
}
return $file;
}
......
......@@ -20,7 +20,7 @@ class ErrorException extends Exception
protected $severity;
/**
* Constructs the exception
* Constructs the exception.
* @link http://php.net/manual/en/errorexception.construct.php
* @param $message [optional]
* @param $code [optional]
......@@ -51,7 +51,6 @@ class ErrorException extends Exception
}
// XDebug has a different key name
$frame['args'] = [];
if (isset($frame['params']) && !isset($frame['args'])) {
$frame['args'] = $frame['params'];
}
......@@ -64,17 +63,7 @@ class ErrorException extends Exception
}
/**
* Gets the exception severity
* @link http://php.net/manual/en/errorexception.getseverity.php
* @return int the severity level of the exception.
*/
final public function getSeverity()
{
return $this->severity;
}
/**
* Returns if error is one of fatal type
* Returns if error is one of fatal type.
*
* @param array $error error got from error_get_last()
* @return bool if error is one of fatal type
......@@ -85,6 +74,16 @@ class ErrorException extends Exception
}
/**
* Gets the exception severity.
* @link http://php.net/manual/en/errorexception.getseverity.php
* @return int the severity level of the exception.
*/
final public function getSeverity()
{
return $this->severity;
}
/**
* @return string the user-friendly name of this exception
*/
public function getName()
......
......@@ -249,9 +249,9 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
* You may override this method to change the default behavior.
* @return array list of attribute names.
*/
public static function attributes()
public function attributes()
{
$class = new ReflectionClass(get_called_class());
$class = new ReflectionClass($this);
$names = [];
foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
if (!$property->isStatic()) {
......
......@@ -65,6 +65,10 @@ class View extends Component
*/
public $renderers;
/**
* @var string the default view file extension. This will be appended to view file names if they don't have file extensions.
*/
public $defaultExtension = '.php';
/**
* @var Theme|array the theme object or the configuration array for creating the theme object.
* If not set, it means theming is not enabled.
*/
......@@ -167,7 +171,7 @@ class View extends Component
}
}
return pathinfo($file, PATHINFO_EXTENSION) === '' ? $file . '.php' : $file;
return pathinfo($file, PATHINFO_EXTENSION) === '' ? $file . $this->defaultExtension : $file;
}
/**
......
......@@ -58,7 +58,7 @@ abstract class Cache extends Component implements \ArrayAccess
* if you don't want to use key prefix. It is recommended that you explicitly set this property to some
* static value if the cached data needs to be shared among multiple applications.
*
* To ensure interoperability, only use alphanumeric characters should be used.
* To ensure interoperability, only alphanumeric characters should be used.
*/
public $keyPrefix;
/**
......
......@@ -93,7 +93,7 @@ class ActiveDataProvider extends BaseDataProvider
}
/**
* @inheritdoc
* {@inheritdoc}
*/
protected function prepareModels()
{
......@@ -111,7 +111,7 @@ class ActiveDataProvider extends BaseDataProvider
}
/**
* @inheritdoc
* {@inheritdoc}
*/
protected function prepareKeys($models)
{
......@@ -150,7 +150,7 @@ class ActiveDataProvider extends BaseDataProvider
}
/**
* @inheritdoc
* {@inheritdoc}
*/
protected function prepareTotalCount()
{
......@@ -162,7 +162,7 @@ class ActiveDataProvider extends BaseDataProvider
}
/**
* @inheritdoc
* {@inheritdoc}
*/
public function setSort($value)
{
......
......@@ -66,7 +66,7 @@ class ArrayDataProvider extends BaseDataProvider
/**
* @inheritdoc
* {@inheritdoc}
*/
protected function prepareModels()
{
......@@ -87,7 +87,7 @@ class ArrayDataProvider extends BaseDataProvider
}
/**
* @inheritdoc
* {@inheritdoc}
*/
protected function prepareKeys($models)
{
......@@ -107,7 +107,7 @@ class ArrayDataProvider extends BaseDataProvider
}
/**
* @inheritdoc
* {@inheritdoc}
*/
protected function prepareTotalCount()
{
......
......@@ -23,6 +23,7 @@ namespace yii\db;
* - [[min()]]: returns the min over the specified column.
* - [[max()]]: returns the max over the specified column.
* - [[scalar()]]: returns the value of the first column in the first row of the query result.
* - [[column()]]: returns the value of the first column in the query result.
* - [[exists()]]: returns a value indicating whether the query result has data or not.
*
* Because ActiveQuery extends from [[Query]], one can use query methods, such as [[where()]],
......
......@@ -27,13 +27,13 @@ use yii\helpers\Inflector;
* @property boolean $isNewRecord Whether the record is new and should be inserted when calling [[save()]].
* @property array $oldAttributes The old attribute values (name-value pairs).
* @property mixed $oldPrimaryKey The old primary key value. An array (column name => column value) is
* returned if the primary key is composite or `$asArray` is true. A string is returned otherwise (null will be
* returned if the key value is null). This property is read-only.
* returned if the primary key is composite. A string is returned otherwise (null will be returned if the key
* value is null). This property is read-only.
* @property array $populatedRelations An array of relation data indexed by relation names. This property is
* read-only.
* @property mixed $primaryKey The primary key value. An array (column name => column value) is returned if
* the primary key is composite or `$asArray` is true. A string is returned otherwise (null will be returned if
* the key value is null). This property is read-only.
* the primary key is composite. A string is returned otherwise (null will be returned if the key value is null).
* This property is read-only.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
......@@ -316,9 +316,9 @@ class ActiveRecord extends Model
* (because another user has modified the data), a [[StaleObjectException]] exception will be thrown,
* and the update or deletion is skipped.
*
* Optimized locking is only supported by [[update()]] and [[delete()]].
* Optimistic locking is only supported by [[update()]] and [[delete()]].
*
* To use optimized locking:
* To use Optimistic locking:
*
* 1. Create a column to store the version number of each row. The column type should be `BIGINT DEFAULT 0`.
* Override this method to return the name of this column.
......@@ -469,9 +469,9 @@ class ActiveRecord extends Model
*
* @param string $class the class name of the related record
* @param array $link the primary-foreign key constraint. The keys of the array refer to
* the columns in the table associated with the `$class` model, while the values of the
* array refer to the corresponding columns in the table associated with this AR class.
* @return ActiveRelation the relation object.
* the attributes of the record associated with the `$class` model, while the values of the
* array refer to the corresponding attributes in **this** AR class.
* @return ActiveRelationInterface the relation object.
*/
public function hasOne($class, $link)
{
......@@ -508,9 +508,9 @@ class ActiveRecord extends Model
*
* @param string $class the class name of the related record
* @param array $link the primary-foreign key constraint. The keys of the array refer to
* the columns in the table associated with the `$class` model, while the values of the
* array refer to the corresponding columns in the table associated with this AR class.
* @return ActiveRelation the relation object.
* the attributes of the record associated with the `$class` model, while the values of the
* array refer to the corresponding attributes in **this** AR class.
* @return ActiveRelationInterface the relation object.
*/
public function hasMany($class, $link)
{
......@@ -570,7 +570,7 @@ class ActiveRecord extends Model
* The default implementation will return all column names of the table associated with this AR class.
* @return array list of attribute names.
*/
public static function attributes()
public function attributes()
{
return array_keys(static::getTableSchema()->columns);
}
......@@ -754,7 +754,7 @@ class ActiveRecord extends Model
* [[EVENT_BEFORE_INSERT]], [[EVENT_AFTER_INSERT]] and [[EVENT_AFTER_VALIDATE]]
* will be raised by the corresponding methods.
*
* Only the [[changedAttributes|changed attribute values]] will be inserted into database.
* Only the [[dirtyAttributes|changed attribute values]] will be inserted into database.
*
* If the table's primary key is auto-incremental and is null during insertion,
* it will be populated with the actual value after insertion.
......@@ -1169,7 +1169,7 @@ class ActiveRecord extends Model
return false;
}
foreach ($this->attributes() as $name) {
$this->_attributes[$name] = $record->_attributes[$name];
$this->_attributes[$name] = isset($record->_attributes[$name]) ? $record->_attributes[$name] : null;
}
$this->_oldAttributes = $this->_attributes;
$this->_related = [];
......@@ -1179,11 +1179,15 @@ class ActiveRecord extends Model
/**
* Returns a value indicating whether the given active record is the same as the current one.
* The comparison is made by comparing the table names and the primary key values of the two active records.
* If one of the records [[isNewRecord|is new]] they are also considered not equal.
* @param ActiveRecord $record record to compare to
* @return boolean whether the two active records refer to the same row in the same database table.
*/
public function equals($record)
{
if ($this->isNewRecord || $record->isNewRecord) {
return false;
}
return $this->tableName() === $record->tableName() && $this->getPrimaryKey() === $record->getPrimaryKey();
}
......@@ -1192,6 +1196,9 @@ class ActiveRecord extends Model
* @param boolean $asArray whether to return the primary key value as an array. If true,
* the return value will be an array with column names as keys and column values as values.
* Note that for composite primary keys, an array will always be returned regardless of this parameter value.
* @property mixed The primary key value. An array (column name => column value) is returned if
* the primary key is composite. A string is returned otherwise (null will be returned if
* the key value is null).
* @return mixed the primary key value. An array (column name => column value) is returned if the primary key
* is composite or `$asArray` is true. A string is returned otherwise (null will be returned if
* the key value is null).
......@@ -1218,6 +1225,9 @@ class ActiveRecord extends Model
* @param boolean $asArray whether to return the primary key value as an array. If true,
* the return value will be an array with column name as key and column value as value.
* If this is false (default), a scalar value will be returned for non-composite primary key.
* @property mixed The old primary key value. An array (column name => column value) is
* returned if the primary key is composite. A string is returned otherwise (null will be
* returned if the key value is null).
* @return mixed the old primary key value. An array (column name => column value) is returned if the primary key
* is composite or `$asArray` is true. A string is returned otherwise (null will be returned if
* the key value is null).
......@@ -1246,7 +1256,7 @@ class ActiveRecord extends Model
public static function create($row)
{
$record = static::instantiate($row);
$columns = array_flip(static::attributes());
$columns = array_flip($record->attributes());
foreach ($row as $name => $value) {
if (isset($columns[$name])) {
$record->_attributes[$name] = $value;
......
......@@ -278,9 +278,13 @@ class Command extends \yii\base\Component
return $n;
} catch (\Exception $e) {
Yii::endProfile($token, __METHOD__);
$message = $e->getMessage() . "\nThe SQL being executed was: $rawSql";
$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
throw new Exception($message, $errorInfo, (int)$e->getCode(), $e);
if ($e instanceof Exception) {
throw $e;
} else {
$message = $e->getMessage() . "\nThe SQL being executed was: $rawSql";
$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
throw new Exception($message, $errorInfo, (int)$e->getCode(), $e);
}
}
}
......@@ -411,9 +415,13 @@ class Command extends \yii\base\Component
return $result;
} catch (\Exception $e) {
Yii::endProfile($token, __METHOD__);
$message = $e->getMessage() . "\nThe SQL being executed was: $rawSql";
$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
throw new Exception($message, $errorInfo, (int)$e->getCode(), $e);
if ($e instanceof Exception) {
throw $e;
} else {
$message = $e->getMessage() . "\nThe SQL being executed was: $rawSql";
$errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
throw new Exception($message, $errorInfo, (int)$e->getCode(), $e);
}
}
}
......
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