mirror of
https://github.com/yiisoft/yii2.git
synced 2025-11-11 19:20:01 +08:00
refactored Model and redis AR to allow drop of RecordSchema
This commit is contained in:
@@ -229,14 +229,13 @@ class Model extends Component implements IteratorAggregate, ArrayAccess
|
|||||||
* You may override this method to change the default behavior.
|
* You may override this method to change the default behavior.
|
||||||
* @return array list of attribute names.
|
* @return array list of attribute names.
|
||||||
*/
|
*/
|
||||||
public function attributes()
|
public static function attributes()
|
||||||
{
|
{
|
||||||
$class = new ReflectionClass($this);
|
$class = new ReflectionClass(get_called_class());
|
||||||
$names = [];
|
$names = [];
|
||||||
foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
|
foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) {
|
||||||
$name = $property->getName();
|
|
||||||
if (!$property->isStatic()) {
|
if (!$property->isStatic()) {
|
||||||
$names[] = $name;
|
$names[] = $property->getName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $names;
|
return $names;
|
||||||
|
|||||||
@@ -568,9 +568,9 @@ class ActiveRecord extends Model
|
|||||||
* The default implementation will return all column names of the table associated with this AR class.
|
* The default implementation will return all column names of the table associated with this AR class.
|
||||||
* @return array list of attribute names.
|
* @return array list of attribute names.
|
||||||
*/
|
*/
|
||||||
public function attributes()
|
public static function attributes()
|
||||||
{
|
{
|
||||||
return array_keys($this->getTableSchema()->columns);
|
return array_keys(static::getTableSchema()->columns);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -580,7 +580,7 @@ class ActiveRecord extends Model
|
|||||||
*/
|
*/
|
||||||
public function hasAttribute($name)
|
public function hasAttribute($name)
|
||||||
{
|
{
|
||||||
return isset($this->_attributes[$name]) || isset($this->getTableSchema()->columns[$name]);
|
return isset($this->_attributes[$name]) || in_array($name, $this->attributes());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1244,7 +1244,7 @@ class ActiveRecord extends Model
|
|||||||
public static function create($row)
|
public static function create($row)
|
||||||
{
|
{
|
||||||
$record = static::instantiate($row);
|
$record = static::instantiate($row);
|
||||||
$columns = static::getTableSchema()->columns;
|
$columns = array_flip(static::attributes());
|
||||||
foreach ($row as $name => $value) {
|
foreach ($row as $name => $value) {
|
||||||
if (isset($columns[$name])) {
|
if (isset($columns[$name])) {
|
||||||
$record->_attributes[$name] = $value;
|
$record->_attributes[$name] = $value;
|
||||||
|
|||||||
@@ -9,22 +9,33 @@ namespace yii\redis;
|
|||||||
|
|
||||||
use yii\base\InvalidConfigException;
|
use yii\base\InvalidConfigException;
|
||||||
use yii\base\NotSupportedException;
|
use yii\base\NotSupportedException;
|
||||||
use yii\db\TableSchema;
|
|
||||||
use yii\helpers\StringHelper;
|
use yii\helpers\StringHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ActiveRecord is the base class for classes representing relational data in terms of objects.
|
* ActiveRecord is the base class for classes representing relational data in terms of objects.
|
||||||
*
|
*
|
||||||
|
* This class implements the ActiveRecord pattern for the [redis](http://redis.io/) key-value store.
|
||||||
|
*
|
||||||
|
* For defining a record a subclass should at least implement the [[attributes()]] method to define
|
||||||
|
* attributes. A primary key can be defined via [[primaryKey()]] which defaults to `id` if not specified.
|
||||||
|
*
|
||||||
|
* The following is an example model called `Customer`:
|
||||||
|
*
|
||||||
|
* ```php
|
||||||
|
* class Customer extends \yii\redis\ActiveRecord
|
||||||
|
* {
|
||||||
|
* public function attributes()
|
||||||
|
* {
|
||||||
|
* return ['id', 'name', 'address', 'registration_date'];
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
* @author Carsten Brandt <mail@cebe.cc>
|
* @author Carsten Brandt <mail@cebe.cc>
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
*/
|
*/
|
||||||
class ActiveRecord extends \yii\db\ActiveRecord
|
class ActiveRecord extends \yii\db\ActiveRecord
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var array cache for TableSchema instances
|
|
||||||
*/
|
|
||||||
private static $_tables = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the database connection used by this AR class.
|
* Returns the database connection used by this AR class.
|
||||||
* By default, the "redis" application component is used as the database connection.
|
* By default, the "redis" application component is used as the database connection.
|
||||||
@@ -36,14 +47,6 @@ class ActiveRecord extends \yii\db\ActiveRecord
|
|||||||
return \Yii::$app->getComponent('redis');
|
return \Yii::$app->getComponent('redis');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public static function findBySql($sql, $params = [])
|
|
||||||
{
|
|
||||||
throw new NotSupportedException('findBySql() is not supported by redis ActiveRecord');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
@@ -61,35 +64,26 @@ class ActiveRecord extends \yii\db\ActiveRecord
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Declares the name of the database table associated with this AR class.
|
* Returns the primary key name(s) for this AR class.
|
||||||
* @return string the table name
|
* This method should be overridden by child classes to define the primary key.
|
||||||
|
*
|
||||||
|
* Note that an array should be returned even when it is a single primary key.
|
||||||
|
*
|
||||||
|
* @return string[] the primary keys of this record.
|
||||||
*/
|
*/
|
||||||
public static function tableName()
|
public static function primaryKey()
|
||||||
{
|
{
|
||||||
return static::getTableSchema()->name;
|
return ['id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is ment to be overridden in redis ActiveRecord subclasses to return a [[RecordSchema]] instance.
|
* Returns the list of all attribute names of the model.
|
||||||
* @return RecordSchema
|
* This method must be overridden by child classes to define available attributes.
|
||||||
* @throws \yii\base\InvalidConfigException
|
* @return array list of attribute names.
|
||||||
*/
|
*/
|
||||||
public static function getRecordSchema()
|
public static function attributes()
|
||||||
{
|
{
|
||||||
throw new InvalidConfigException(__CLASS__.'::getRecordSchema() needs to be overridden in subclasses and return a RecordSchema.');
|
throw new InvalidConfigException('The attributes() method of redis ActiveRecord has to be implemented by child classes.');
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the schema information of the DB table associated with this AR class.
|
|
||||||
* @return TableSchema the schema information of the DB table associated with this AR class.
|
|
||||||
*/
|
|
||||||
public static function getTableSchema()
|
|
||||||
{
|
|
||||||
$class = get_called_class();
|
|
||||||
if (isset(self::$_tables[$class])) {
|
|
||||||
return self::$_tables[$class];
|
|
||||||
}
|
|
||||||
return self::$_tables[$class] = static::getRecordSchema();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -299,6 +293,22 @@ class ActiveRecord extends \yii\db\ActiveRecord
|
|||||||
return md5(json_encode($key));
|
return md5(json_encode($key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public static function getTableSchema()
|
||||||
|
{
|
||||||
|
throw new NotSupportedException('getTableSchema() is not supported by redis ActiveRecord');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public static function findBySql($sql, $params = [])
|
||||||
|
{
|
||||||
|
throw new NotSupportedException('findBySql() is not supported by redis ActiveRecord');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a value indicating whether the specified operation is transactional in the current [[scenario]].
|
* Returns a value indicating whether the specified operation is transactional in the current [[scenario]].
|
||||||
* This method will always return false as transactional operations are not supported by redis.
|
* This method will always return false as transactional operations are not supported by redis.
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ use yii\helpers\Inflector;
|
|||||||
* @property string $driverName Name of the DB driver. This property is read-only.
|
* @property string $driverName Name of the DB driver. This property is read-only.
|
||||||
* @property boolean $isActive Whether the DB connection is established. This property is read-only.
|
* @property boolean $isActive Whether the DB connection is established. This property is read-only.
|
||||||
* @property LuaScriptBuilder $luaScriptBuilder This property is read-only.
|
* @property LuaScriptBuilder $luaScriptBuilder This property is read-only.
|
||||||
* @property Transaction $transaction The currently active transaction. Null if no active transaction. This
|
|
||||||
* property is read-only.
|
|
||||||
*
|
*
|
||||||
* @author Carsten Brandt <mail@cebe.cc>
|
* @author Carsten Brandt <mail@cebe.cc>
|
||||||
* @since 2.0
|
* @since 2.0
|
||||||
@@ -201,10 +199,6 @@ class Connection extends Component
|
|||||||
'ZSCORE', // key member Get the score associated with the given member in a sorted set
|
'ZSCORE', // key member Get the score associated with the given member in a sorted set
|
||||||
'ZUNIONSTORE', // destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] Add multiple sorted sets and store the resulting sorted set in a new key
|
'ZUNIONSTORE', // destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] Add multiple sorted sets and store the resulting sorted set in a new key
|
||||||
];
|
];
|
||||||
/**
|
|
||||||
* @var Transaction the currently active transaction
|
|
||||||
*/
|
|
||||||
private $_transaction;
|
|
||||||
/**
|
/**
|
||||||
* @var resource redis socket connection
|
* @var resource redis socket connection
|
||||||
*/
|
*/
|
||||||
@@ -296,27 +290,6 @@ class Connection extends Component
|
|||||||
$this->trigger(self::EVENT_AFTER_OPEN);
|
$this->trigger(self::EVENT_AFTER_OPEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the currently active transaction.
|
|
||||||
* @return Transaction the currently active transaction. Null if no active transaction.
|
|
||||||
*/
|
|
||||||
public function getTransaction()
|
|
||||||
{
|
|
||||||
return $this->_transaction && $this->_transaction->isActive ? $this->_transaction : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts a transaction.
|
|
||||||
* @return Transaction the transaction initiated
|
|
||||||
*/
|
|
||||||
public function beginTransaction()
|
|
||||||
{
|
|
||||||
$this->open();
|
|
||||||
$this->_transaction = new Transaction(['db' => $this]);
|
|
||||||
$this->_transaction->begin();
|
|
||||||
return $this->_transaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of the DB driver for the current [[dsn]].
|
* Returns the name of the DB driver for the current [[dsn]].
|
||||||
* @return string name of the DB driver
|
* @return string name of the DB driver
|
||||||
|
|||||||
@@ -129,6 +129,10 @@ class LuaScriptBuilder extends \yii\base\Object
|
|||||||
*/
|
*/
|
||||||
private function build($query, $buildResult, $return)
|
private function build($query, $buildResult, $return)
|
||||||
{
|
{
|
||||||
|
if (!empty($query->orderBy)) {
|
||||||
|
throw new NotSupportedException('orderBy is currently not supported by redis ActiveRecord.');
|
||||||
|
}
|
||||||
|
|
||||||
$columns = [];
|
$columns = [];
|
||||||
if ($query->where !== null) {
|
if ($query->where !== null) {
|
||||||
$condition = $this->buildCondition($query->where, $columns);
|
$condition = $this->buildCondition($query->where, $columns);
|
||||||
@@ -139,6 +143,7 @@ class LuaScriptBuilder extends \yii\base\Object
|
|||||||
$start = $query->offset === null ? 0 : $query->offset;
|
$start = $query->offset === null ? 0 : $query->offset;
|
||||||
$limitCondition = 'i>' . $start . ($query->limit === null ? '' : ' and i<=' . ($start + $query->limit));
|
$limitCondition = 'i>' . $start . ($query->limit === null ? '' : ' and i<=' . ($start + $query->limit));
|
||||||
|
|
||||||
|
/** @var ActiveRecord $modelClass */
|
||||||
$modelClass = $query->modelClass;
|
$modelClass = $query->modelClass;
|
||||||
$key = $this->quoteValue($modelClass::tableName());
|
$key = $this->quoteValue($modelClass::tableName());
|
||||||
$loadColumnValues = '';
|
$loadColumnValues = '';
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* @link http://www.yiiframework.com/
|
|
||||||
* @copyright Copyright (c) 2008 Yii Software LLC
|
|
||||||
* @license http://www.yiiframework.com/license/
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace yii\redis;
|
|
||||||
|
|
||||||
use yii\base\InvalidConfigException;
|
|
||||||
use yii\db\TableSchema;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class RecordSchema defines the data schema for a redis active record
|
|
||||||
*
|
|
||||||
* As there is no schema in a redis DB this class is used to define one.
|
|
||||||
*
|
|
||||||
* @package yii\db\redis
|
|
||||||
*/
|
|
||||||
class RecordSchema extends TableSchema
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var string[] column names.
|
|
||||||
*/
|
|
||||||
public $columns = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string the column type
|
|
||||||
*/
|
|
||||||
public function getColumn($name)
|
|
||||||
{
|
|
||||||
parent::getColumn($name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function init()
|
|
||||||
{
|
|
||||||
if (empty($this->name)) {
|
|
||||||
throw new InvalidConfigException('name of RecordSchema must not be empty.');
|
|
||||||
}
|
|
||||||
if (empty($this->primaryKey)) {
|
|
||||||
throw new InvalidConfigException('primaryKey of RecordSchema must not be empty.');
|
|
||||||
}
|
|
||||||
if (!is_array($this->primaryKey)) {
|
|
||||||
$this->primaryKey = [$this->primaryKey];
|
|
||||||
}
|
|
||||||
foreach($this->primaryKey as $pk) {
|
|
||||||
if (!isset($this->columns[$pk])) {
|
|
||||||
throw new InvalidConfigException('primaryKey '.$pk.' is not a colum of RecordSchema.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -64,13 +64,13 @@ class UniqueValidator extends Validator
|
|||||||
$className = $this->className === null ? get_class($object) : $this->className;
|
$className = $this->className === null ? get_class($object) : $this->className;
|
||||||
$attributeName = $this->attributeName === null ? $attribute : $this->attributeName;
|
$attributeName = $this->attributeName === null ? $attribute : $this->attributeName;
|
||||||
|
|
||||||
$table = $className::getTableSchema();
|
$attributes = $className::attributes();
|
||||||
if (($column = $table->getColumn($attributeName)) === null) {
|
if (!in_array($attribute, $attributes)) {
|
||||||
throw new InvalidConfigException("Table '{$table->name}' does not have a column named '$attributeName'.");
|
throw new InvalidConfigException("'$className' does not have an attribute named '$attributeName'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = $className::find();
|
$query = $className::find();
|
||||||
$query->where([$column->name => $value]);
|
$query->where([$attribute => $value]);
|
||||||
|
|
||||||
if (!$object instanceof ActiveRecord || $object->getIsNewRecord()) {
|
if (!$object instanceof ActiveRecord || $object->getIsNewRecord()) {
|
||||||
// if current $object isn't in the database yet then it's OK just to call exists()
|
// if current $object isn't in the database yet then it's OK just to call exists()
|
||||||
@@ -82,7 +82,7 @@ class UniqueValidator extends Validator
|
|||||||
|
|
||||||
$n = count($objects);
|
$n = count($objects);
|
||||||
if ($n === 1) {
|
if ($n === 1) {
|
||||||
if ($column->isPrimaryKey) {
|
if (in_array($attribute, $className::primaryKey())) {
|
||||||
// primary key is modified and not unique
|
// primary key is modified and not unique
|
||||||
$exists = $object->getOldPrimaryKey() != $object->getPrimaryKey();
|
$exists = $object->getOldPrimaryKey() != $object->getPrimaryKey();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
namespace yiiunit\data\ar\redis;
|
namespace yiiunit\data\ar\redis;
|
||||||
|
|
||||||
use yii\redis\RecordSchema;
|
|
||||||
|
|
||||||
class Customer extends ActiveRecord
|
class Customer extends ActiveRecord
|
||||||
{
|
{
|
||||||
const STATUS_ACTIVE = 1;
|
const STATUS_ACTIVE = 1;
|
||||||
@@ -11,6 +9,11 @@ class Customer extends ActiveRecord
|
|||||||
|
|
||||||
public $status2;
|
public $status2;
|
||||||
|
|
||||||
|
public static function attributes()
|
||||||
|
{
|
||||||
|
return ['id', 'email', 'name', 'address', 'status'];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return \yii\redis\ActiveRelation
|
* @return \yii\redis\ActiveRelation
|
||||||
*/
|
*/
|
||||||
@@ -23,19 +26,4 @@ class Customer extends ActiveRecord
|
|||||||
{
|
{
|
||||||
$query->andWhere(['status' => 1]);
|
$query->andWhere(['status' => 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getRecordSchema()
|
|
||||||
{
|
|
||||||
return new RecordSchema(array(
|
|
||||||
'name' => 'customer',
|
|
||||||
'primaryKey' => array('id'),
|
|
||||||
'columns' => array(
|
|
||||||
'id' => 'integer',
|
|
||||||
'email' => 'string',
|
|
||||||
'name' => 'string',
|
|
||||||
'address' => 'string',
|
|
||||||
'status' => 'integer'
|
|
||||||
)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -2,21 +2,10 @@
|
|||||||
|
|
||||||
namespace yiiunit\data\ar\redis;
|
namespace yiiunit\data\ar\redis;
|
||||||
|
|
||||||
use yii\redis\RecordSchema;
|
|
||||||
|
|
||||||
class Item extends ActiveRecord
|
class Item extends ActiveRecord
|
||||||
{
|
{
|
||||||
public static function getRecordSchema()
|
public static function attributes()
|
||||||
{
|
{
|
||||||
return new RecordSchema([
|
return ['id', 'name', 'category_id'];
|
||||||
'name' => 'item',
|
|
||||||
'primaryKey' => ['id'],
|
|
||||||
'sequenceName' => 'id',
|
|
||||||
'columns' => [
|
|
||||||
'id' => 'integer',
|
|
||||||
'name' => 'string',
|
|
||||||
'category_id' => 'integer'
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,10 +2,13 @@
|
|||||||
|
|
||||||
namespace yiiunit\data\ar\redis;
|
namespace yiiunit\data\ar\redis;
|
||||||
|
|
||||||
use yii\redis\RecordSchema;
|
|
||||||
|
|
||||||
class Order extends ActiveRecord
|
class Order extends ActiveRecord
|
||||||
{
|
{
|
||||||
|
public static function attributes()
|
||||||
|
{
|
||||||
|
return ['id', 'customer_id', 'create_time', 'total'];
|
||||||
|
}
|
||||||
|
|
||||||
public function getCustomer()
|
public function getCustomer()
|
||||||
{
|
{
|
||||||
return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
|
return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
|
||||||
@@ -40,20 +43,4 @@ class Order extends ActiveRecord
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static function getRecordSchema()
|
|
||||||
{
|
|
||||||
return new RecordSchema(array(
|
|
||||||
'name' => 'orders',
|
|
||||||
'primaryKey' => ['id'],
|
|
||||||
'columns' => array(
|
|
||||||
'id' => 'integer',
|
|
||||||
'customer_id' => 'integer',
|
|
||||||
'create_time' => 'integer',
|
|
||||||
'total' => 'decimal',
|
|
||||||
)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,16 @@ use yii\redis\RecordSchema;
|
|||||||
|
|
||||||
class OrderItem extends ActiveRecord
|
class OrderItem extends ActiveRecord
|
||||||
{
|
{
|
||||||
|
public static function primaryKey()
|
||||||
|
{
|
||||||
|
return ['order_id', 'item_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function attributes()
|
||||||
|
{
|
||||||
|
return ['order_id', 'item_id', 'quantity', 'subtotal'];
|
||||||
|
}
|
||||||
|
|
||||||
public function getOrder()
|
public function getOrder()
|
||||||
{
|
{
|
||||||
return $this->hasOne(Order::className(), ['id' => 'order_id']);
|
return $this->hasOne(Order::className(), ['id' => 'order_id']);
|
||||||
@@ -15,18 +25,4 @@ class OrderItem extends ActiveRecord
|
|||||||
{
|
{
|
||||||
return $this->hasOne(Item::className(), ['id' => 'item_id']);
|
return $this->hasOne(Item::className(), ['id' => 'item_id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getRecordSchema()
|
|
||||||
{
|
|
||||||
return new RecordSchema(array(
|
|
||||||
'name' => 'order_item',
|
|
||||||
'primaryKey' => ['order_id', 'item_id'],
|
|
||||||
'columns' => array(
|
|
||||||
'order_id' => 'integer',
|
|
||||||
'item_id' => 'integer',
|
|
||||||
'quantity' => 'integer',
|
|
||||||
'subtotal' => 'decimal',
|
|
||||||
)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user