Browse Source
* upstream/master: (91 commits) fixed init.bat paths renamed backstage → backend Fixed test break. Response WIP coding style fix. [1] Redone missing code. [2] Added empty line at the end of file [3] Removed exception. Updated code style. braces on the same line for control statements. Removed unused columsn from find constraint sql. Fixed typo. Added extra schema check for when a foreign table is not in the same schema. Updated indentation to conform to other classes. Multilevel Items Fixes issue #514. Removed the config setting that should not have been commited. Removed false exception catching. Removed custom pgsql PDO and added defaultSchema as public property. CS fixes. Minor refactoring of DbMessageSource. Response WIP Minor refactoring of t(). Added Jsonable support. Fixes #13. Implement DB message source. Yii::t() minor fix. ...tags/2.0.0-beta
Antonio Ramirez
12 years ago
150 changed files with 4527 additions and 3045 deletions
@ -1,6 +1,6 @@
|
||||
<?php |
||||
|
||||
namespace backstage\controllers; |
||||
namespace backend\controllers; |
||||
|
||||
use Yii; |
||||
use yii\web\Controller; |
@ -0,0 +1,22 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\base; |
||||
|
||||
/** |
||||
* Jsonable should be implemented by classes that need to be represented in JSON format. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
interface Jsonable |
||||
{ |
||||
/** |
||||
* @return string the JSON representation of this object |
||||
*/ |
||||
public function toJson(); |
||||
} |
@ -0,0 +1,17 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\console; |
||||
|
||||
/** |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class Response extends \yii\base\Response |
||||
{ |
||||
|
||||
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,41 @@
|
||||
<?php |
||||
|
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\db\pgsql; |
||||
|
||||
/** |
||||
* QueryBuilder is the query builder for PostgreSQL databases. |
||||
* |
||||
* @author Gevik Babakhani <gevikb@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class QueryBuilder extends \yii\db\QueryBuilder |
||||
{ |
||||
|
||||
/** |
||||
* @var array mapping from abstract column types (keys) to physical column types (values). |
||||
*/ |
||||
public $typeMap = array( |
||||
Schema::TYPE_PK => 'serial not null primary key', |
||||
Schema::TYPE_STRING => 'varchar', |
||||
Schema::TYPE_TEXT => 'text', |
||||
Schema::TYPE_SMALLINT => 'smallint', |
||||
Schema::TYPE_INTEGER => 'integer', |
||||
Schema::TYPE_BIGINT => 'bigint', |
||||
Schema::TYPE_FLOAT => 'double precision', |
||||
Schema::TYPE_DECIMAL => 'numeric', |
||||
Schema::TYPE_DATETIME => 'timestamp', |
||||
Schema::TYPE_TIMESTAMP => 'timestamp', |
||||
Schema::TYPE_TIME => 'time', |
||||
Schema::TYPE_DATE => 'date', |
||||
Schema::TYPE_BINARY => 'bytea', |
||||
Schema::TYPE_BOOLEAN => 'boolean', |
||||
Schema::TYPE_MONEY => 'numeric(19,4)', |
||||
); |
||||
|
||||
} |
@ -0,0 +1,288 @@
|
||||
<?php |
||||
|
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\db\pgsql; |
||||
|
||||
use yii\db\TableSchema; |
||||
use yii\db\ColumnSchema; |
||||
|
||||
/** |
||||
* Schema is the class for retrieving metadata from a PostgreSQL database |
||||
* (version 9.x and above). |
||||
* |
||||
* @author Gevik Babakhani <gevikb@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class Schema extends \yii\db\Schema |
||||
{ |
||||
|
||||
/** |
||||
* The default schema used for the current session. |
||||
* @var string |
||||
*/ |
||||
public $defaultSchema = 'public'; |
||||
|
||||
/** |
||||
* @var array mapping from physical column types (keys) to abstract |
||||
* column types (values) |
||||
*/ |
||||
public $typeMap = array( |
||||
'abstime' => self::TYPE_TIMESTAMP, |
||||
'bit' => self::TYPE_STRING, |
||||
'boolean' => self::TYPE_BOOLEAN, |
||||
'box' => self::TYPE_STRING, |
||||
'character' => self::TYPE_STRING, |
||||
'bytea' => self::TYPE_BINARY, |
||||
'char' => self::TYPE_STRING, |
||||
'cidr' => self::TYPE_STRING, |
||||
'circle' => self::TYPE_STRING, |
||||
'date' => self::TYPE_DATE, |
||||
'real' => self::TYPE_FLOAT, |
||||
'double precision' => self::TYPE_DECIMAL, |
||||
'inet' => self::TYPE_STRING, |
||||
'smallint' => self::TYPE_SMALLINT, |
||||
'integer' => self::TYPE_INTEGER, |
||||
'bigint' => self::TYPE_BIGINT, |
||||
'interval' => self::TYPE_STRING, |
||||
'json' => self::TYPE_STRING, |
||||
'line' => self::TYPE_STRING, |
||||
'macaddr' => self::TYPE_STRING, |
||||
'money' => self::TYPE_MONEY, |
||||
'name' => self::TYPE_STRING, |
||||
'numeric' => self::TYPE_STRING, |
||||
'numrange' => self::TYPE_DECIMAL, |
||||
'oid' => self::TYPE_BIGINT, // should not be used. it's pg internal! |
||||
'path' => self::TYPE_STRING, |
||||
'point' => self::TYPE_STRING, |
||||
'polygon' => self::TYPE_STRING, |
||||
'text' => self::TYPE_TEXT, |
||||
'time without time zone' => self::TYPE_TIME, |
||||
'timestamp without time zone' => self::TYPE_TIMESTAMP, |
||||
'timestamp with time zone' => self::TYPE_TIMESTAMP, |
||||
'time with time zone' => self::TYPE_TIMESTAMP, |
||||
'unknown' => self::TYPE_STRING, |
||||
'uuid' => self::TYPE_STRING, |
||||
'bit varying' => self::TYPE_STRING, |
||||
'character varying' => self::TYPE_STRING, |
||||
'xml' => self::TYPE_STRING |
||||
); |
||||
|
||||
/** |
||||
* Creates a query builder for the PostgreSQL database. |
||||
* @return QueryBuilder query builder instance |
||||
*/ |
||||
public function createQueryBuilder() |
||||
{ |
||||
return new QueryBuilder($this->db); |
||||
} |
||||
|
||||
/** |
||||
* Resolves the table name and schema name (if any). |
||||
* @param TableSchema $table the table metadata object |
||||
* @param string $name the table name |
||||
*/ |
||||
protected function resolveTableNames($table, $name) |
||||
{ |
||||
$parts = explode('.', str_replace('"', '', $name)); |
||||
if (isset($parts[1])) { |
||||
$table->schemaName = $parts[0]; |
||||
$table->name = $parts[1]; |
||||
} else { |
||||
$table->name = $parts[0]; |
||||
} |
||||
if ($table->schemaName === null) { |
||||
$table->schemaName = $this->defaultSchema; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Quotes a table name for use in a query. |
||||
* A simple table name has no schema prefix. |
||||
* @param string $name table name |
||||
* @return string the properly quoted table name |
||||
*/ |
||||
public function quoteSimpleTableName($name) |
||||
{ |
||||
return strpos($name, '"') !== false ? $name : '"' . $name . '"'; |
||||
} |
||||
|
||||
/** |
||||
* Loads the metadata for the specified table. |
||||
* @param string $name table name |
||||
* @return TableSchema|null driver dependent table metadata. Null if the table does not exist. |
||||
*/ |
||||
public function loadTableSchema($name) |
||||
{ |
||||
$table = new TableSchema(); |
||||
$this->resolveTableNames($table, $name); |
||||
if ($this->findColumns($table)) { |
||||
$this->findConstraints($table); |
||||
return $table; |
||||
} else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Collects the foreign key column details for the given table. |
||||
* @param TableSchema $table the table metadata |
||||
*/ |
||||
protected function findConstraints($table) |
||||
{ |
||||
|
||||
$tableName = $this->quoteValue($table->name); |
||||
$tableSchema = $this->quoteValue($table->schemaName); |
||||
|
||||
//We need to extract the constraints de hard way since: |
||||
//http://www.postgresql.org/message-id/26677.1086673982@sss.pgh.pa.us |
||||
|
||||
$sql = <<<SQL |
||||
select |
||||
(select string_agg(attname,',') attname from pg_attribute where attrelid=ct.conrelid and attnum = any(ct.conkey)) as columns, |
||||
fc.relname as foreign_table_name, |
||||
fns.nspname as foreign_table_schema, |
||||
(select string_agg(attname,',') attname from pg_attribute where attrelid=ct.confrelid and attnum = any(ct.confkey)) as foreign_columns |
||||
from |
||||
pg_constraint ct |
||||
inner join pg_class c on c.oid=ct.conrelid |
||||
inner join pg_namespace ns on c.relnamespace=ns.oid |
||||
left join pg_class fc on fc.oid=ct.confrelid |
||||
left join pg_namespace fns on fc.relnamespace=fns.oid |
||||
|
||||
where |
||||
ct.contype='f' |
||||
and c.relname={$tableName} |
||||
and ns.nspname={$tableSchema} |
||||
SQL; |
||||
|
||||
$constraints = $this->db->createCommand($sql)->queryAll(); |
||||
foreach ($constraints as $constraint) { |
||||
$columns = explode(',', $constraint['columns']); |
||||
$fcolumns = explode(',', $constraint['foreign_columns']); |
||||
if ($constraint['foreign_table_schema'] !== $this->defaultSchema) { |
||||
$foreignTable = $constraint['foreign_table_schema'] . '.' . $constraint['foreign_table_name']; |
||||
} else { |
||||
$foreignTable = $constraint['foreign_table_name']; |
||||
} |
||||
$citem = array($foreignTable); |
||||
foreach ($columns as $idx => $column) { |
||||
$citem[] = array($fcolumns[$idx] => $column); |
||||
} |
||||
$table->foreignKeys[] = $citem; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Collects the metadata of table columns. |
||||
* @param TableSchema $table the table metadata |
||||
* @return boolean whether the table exists in the database |
||||
*/ |
||||
protected function findColumns($table) |
||||
{ |
||||
$tableName = $this->db->quoteValue($table->name); |
||||
$schemaName = $this->db->quoteValue($table->schemaName); |
||||
$sql = <<<SQL |
||||
SELECT |
||||
current_database() as table_catalog, |
||||
d.nspname AS table_schema, |
||||
c.relname AS table_name, |
||||
a.attname AS column_name, |
||||
t.typname AS data_type, |
||||
a.attlen AS character_maximum_length, |
||||
pg_catalog.col_description(c.oid, a.attnum) AS column_comment, |
||||
a.atttypmod AS modifier, |
||||
a.attnotnull = false AS is_nullable, |
||||
CAST(pg_get_expr(ad.adbin, ad.adrelid) AS varchar) AS column_default, |
||||
coalesce(pg_get_expr(ad.adbin, ad.adrelid) ~ 'nextval',false) AS is_autoinc, |
||||
array_to_string((select array_agg(enumlabel) from pg_enum where enumtypid=a.atttypid)::varchar[],',') as enum_values, |
||||
CASE atttypid |
||||
WHEN 21 /*int2*/ THEN 16 |
||||
WHEN 23 /*int4*/ THEN 32 |
||||
WHEN 20 /*int8*/ THEN 64 |
||||
WHEN 1700 /*numeric*/ THEN |
||||
CASE WHEN atttypmod = -1 |
||||
THEN null |
||||
ELSE ((atttypmod - 4) >> 16) & 65535 |
||||
END |
||||
WHEN 700 /*float4*/ THEN 24 /*FLT_MANT_DIG*/ |
||||
WHEN 701 /*float8*/ THEN 53 /*DBL_MANT_DIG*/ |
||||
ELSE null |
||||
END AS numeric_precision, |
||||
CASE |
||||
WHEN atttypid IN (21, 23, 20) THEN 0 |
||||
WHEN atttypid IN (1700) THEN |
||||
CASE |
||||
WHEN atttypmod = -1 THEN null |
||||
ELSE (atttypmod - 4) & 65535 |
||||
END |
||||
ELSE null |
||||
END AS numeric_scale, |
||||
CAST( |
||||
information_schema._pg_char_max_length(information_schema._pg_truetypid(a, t), information_schema._pg_truetypmod(a, t)) |
||||
AS numeric |
||||
) AS size, |
||||
a.attnum = any (ct.conkey) as is_pkey |
||||
FROM |
||||
pg_class c |
||||
LEFT JOIN pg_attribute a ON a.attrelid = c.oid |
||||
LEFT JOIN pg_attrdef ad ON a.attrelid = ad.adrelid AND a.attnum = ad.adnum |
||||
LEFT JOIN pg_type t ON a.atttypid = t.oid |
||||
LEFT JOIN pg_namespace d ON d.oid = c.relnamespace |
||||
LEFT join pg_constraint ct on ct.conrelid=c.oid and ct.contype='p' |
||||
WHERE |
||||
a.attnum > 0 |
||||
and c.relname = {$tableName} |
||||
and d.nspname = {$schemaName} |
||||
ORDER BY |
||||
a.attnum; |
||||
SQL; |
||||
|
||||
$columns = $this->db->createCommand($sql)->queryAll(); |
||||
foreach ($columns as $column) { |
||||
$column = $this->loadColumnSchema($column); |
||||
$table->columns[$column->name] = $column; |
||||
if ($column->isPrimaryKey === true) { |
||||
$table->primaryKey[] = $column->name; |
||||
if ($table->sequenceName === null && preg_match("/nextval\('\w+'(::regclass)?\)/", $column->defaultValue) === 1) { |
||||
$table->sequenceName = preg_replace(array('/nextval/', '/::/', '/regclass/', '/\'\)/', '/\(\'/'), '', $column->defaultValue); |
||||
} |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Loads the column information into a [[ColumnSchema]] object. |
||||
* @param array $info column information |
||||
* @return ColumnSchema the column schema object |
||||
*/ |
||||
protected function loadColumnSchema($info) |
||||
{ |
||||
$column = new ColumnSchema(); |
||||
$column->allowNull = $info['is_nullable']; |
||||
$column->autoIncrement = $info['is_autoinc']; |
||||
$column->comment = $info['column_comment']; |
||||
$column->dbType = $info['data_type']; |
||||
$column->defaultValue = $info['column_default']; |
||||
$column->enumValues = explode(',', str_replace(array("''"), array("'"), $info['enum_values'])); |
||||
$column->unsigned = false; // has no meanining in PG |
||||
$column->isPrimaryKey = $info['is_pkey']; |
||||
$column->name = $info['column_name']; |
||||
$column->precision = $info['numeric_precision']; |
||||
$column->scale = $info['numeric_scale']; |
||||
$column->size = $info['size']; |
||||
|
||||
if (isset($this->typeMap[$column->dbType])) { |
||||
$column->type = $this->typeMap[$column->dbType]; |
||||
} else { |
||||
$column->type = self::TYPE_STRING; |
||||
} |
||||
$column->phpType = $this->getColumnPhpType($column); |
||||
return $column; |
||||
} |
||||
} |
@ -0,0 +1,158 @@
|
||||
<?php |
||||
/** |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\i18n; |
||||
|
||||
use Yii; |
||||
use yii\base\InvalidConfigException; |
||||
use yii\helpers\ArrayHelper; |
||||
use yii\caching\Cache; |
||||
use yii\db\Connection; |
||||
use yii\db\Query; |
||||
|
||||
/** |
||||
* DbMessageSource extends [[MessageSource]] and represents a message source that stores translated |
||||
* messages in database. |
||||
* |
||||
* The database must contain the following two tables: |
||||
* |
||||
* ~~~ |
||||
* CREATE TABLE tbl_source_message ( |
||||
* id INTEGER PRIMARY KEY, |
||||
* category VARCHAR(32), |
||||
* message TEXT |
||||
* ); |
||||
* |
||||
* CREATE TABLE tbl_message ( |
||||
* id INTEGER, |
||||
* language VARCHAR(16), |
||||
* translation TEXT, |
||||
* PRIMARY KEY (id, language), |
||||
* CONSTRAINT fk_message_source_message FOREIGN KEY (id) |
||||
* REFERENCES tbl_source_message (id) ON DELETE CASCADE ON UPDATE RESTRICT |
||||
* ); |
||||
* ~~~ |
||||
* |
||||
* The `tbl_source_message` table stores the messages to be translated, and the `tbl_message` table stores |
||||
* the translated messages. The name of these two tables can be customized by setting [[sourceMessageTable]] |
||||
* and [[messageTable]], respectively. |
||||
* |
||||
* @author resurtm <resurtm@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class DbMessageSource extends MessageSource |
||||
{ |
||||
/** |
||||
* Prefix which would be used when generating cache key. |
||||
*/ |
||||
const CACHE_KEY_PREFIX = 'DbMessageSource'; |
||||
|
||||
/** |
||||
* @var Connection|string the DB connection object or the application component ID of the DB connection. |
||||
* After the DbMessageSource object is created, if you want to change this property, you should only assign |
||||
* it with a DB connection object. |
||||
*/ |
||||
public $db = 'db'; |
||||
/** |
||||
* @var Cache|string the cache object or the application component ID of the cache object. |
||||
* The messages data will be cached using this cache object. Note, this property has meaning only |
||||
* in case [[cachingDuration]] set to non-zero value. |
||||
* After the DbMessageSource object is created, if you want to change this property, you should only assign |
||||
* it with a cache object. |
||||
*/ |
||||
public $cache = 'cache'; |
||||
/** |
||||
* @var string the name of the source message table. |
||||
*/ |
||||
public $sourceMessageTable = 'tbl_source_message'; |
||||
/** |
||||
* @var string the name of the translated message table. |
||||
*/ |
||||
public $messageTable = 'tbl_message'; |
||||
/** |
||||
* @var integer the time in seconds that the messages can remain valid in cache. |
||||
* Use 0 to indicate that the cached data will never expire. |
||||
* @see enableCaching |
||||
*/ |
||||
public $cachingDuration = 0; |
||||
/** |
||||
* @var boolean whether to enable caching translated messages |
||||
*/ |
||||
public $enableCaching = false; |
||||
|
||||
/** |
||||
* Initializes the DbMessageSource component. |
||||
* This method will initialize the [[db]] property to make sure it refers to a valid DB connection. |
||||
* Configured [[cache]] component would also be initialized. |
||||
* @throws InvalidConfigException if [[db]] is invalid or [[cache]] is invalid. |
||||
*/ |
||||
public function init() |
||||
{ |
||||
parent::init(); |
||||
if (is_string($this->db)) { |
||||
$this->db = Yii::$app->getComponent($this->db); |
||||
} |
||||
if (!$this->db instanceof Connection) { |
||||
throw new InvalidConfigException("DbMessageSource::db must be either a DB connection instance or the application component ID of a DB connection."); |
||||
} |
||||
if ($this->enableCaching) { |
||||
if (is_string($this->cache)) { |
||||
$this->cache = Yii::$app->getComponent($this->cache); |
||||
} |
||||
if (!$this->cache instanceof Cache) { |
||||
throw new InvalidConfigException("DbMessageSource::cache must be either a cache object or the application component ID of the cache object."); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Loads the message translation for the specified language and category. |
||||
* Child classes should override this method to return the message translations of |
||||
* the specified language and category. |
||||
* @param string $category the message category |
||||
* @param string $language the target language |
||||
* @return array the loaded messages. The keys are original messages, and the values |
||||
* are translated messages. |
||||
*/ |
||||
protected function loadMessages($category, $language) |
||||
{ |
||||
if ($this->enableCaching) { |
||||
$key = array( |
||||
__CLASS__, |
||||
$category, |
||||
$language, |
||||
); |
||||
$messages = $this->cache->get($key); |
||||
if ($messages === false) { |
||||
$messages = $this->loadMessagesFromDb($category, $language); |
||||
$this->cache->set($key, $messages, $this->cachingDuration); |
||||
} |
||||
return $messages; |
||||
} else { |
||||
return $this->loadMessagesFromDb($category, $language); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Loads the messages from database. |
||||
* You may override this method to customize the message storage in the database. |
||||
* @param string $category the message category. |
||||
* @param string $language the target language. |
||||
* @return array the messages loaded from database. |
||||
*/ |
||||
protected function loadMessagesFromDb($category, $language) |
||||
{ |
||||
$query = new Query(); |
||||
$messages = $query->select(array('t1.message message', 't2.translation translation')) |
||||
->from(array($this->sourceMessageTable . ' t1', $this->messageTable . ' t2')) |
||||
->where('t1.id = t2.id AND t1.category = :category AND t2.language = :language') |
||||
->params(array(':category' => $category, ':language' => $language)) |
||||
->createCommand($this->db) |
||||
->queryAll(); |
||||
return ArrayHelper::map($messages, 'message', 'translation'); |
||||
} |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue