Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yii2
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
PSDI Army
yii2
Commits
ed513b4d
Commit
ed513b4d
authored
Mar 25, 2012
by
Qiang Xue
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
...
parent
5ddc1ba1
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
145 additions
and
147 deletions
+145
-147
ActiveFinder.php
framework/db/ar/ActiveFinder.php
+109
-115
ActiveQuery.php
framework/db/ar/ActiveQuery.php
+10
-18
ActiveRecord.php
framework/db/ar/ActiveRecord.php
+10
-10
JoinElement.php
framework/db/ar/JoinElement.php
+0
-4
ActiveRecordTest.php
tests/unit/framework/db/ar/ActiveRecordTest.php
+16
-0
No files found.
framework/db/ar/ActiveFinder.php
View file @
ed513b4d
...
...
@@ -17,32 +17,6 @@ use yii\db\Exception;
/**
* ActiveFinder.php is ...
* todo: lazy loading
* todo: clean up joinOnly and select=false
* todo: refactor code
* todo: count with
* todo: findBySql and lazy loading cannot apply scopes for primary table
*
* Four cases:
* 1. normal eager loading
* 2. eager loading, base limited and has many
* 3. findBySql and eager loading
* 4. lazy loading
*
* Build a join tree
* Update join tree
* Case 2:
* Find PKs for primary table
* Modify main query with the found PK, reset limit/offset
* Case 3:
* Find records by SQL
* Reset main query and set WHERE with the found PK
* Set root.records = the found records
* Case 4:
* Set root.records = the primary record
* Generate join query
* Case 4:
* If
*
* @property integer $count
*
...
...
@@ -64,18 +38,18 @@ class ActiveFinder extends \yii\base\Object
/**
* @param ActiveQuery $query
*/
public
function
find
Records
(
$query
)
public
function
find
(
$query
,
$returnScalar
=
false
)
{
if
(
!
empty
(
$query
->
with
))
{
return
$this
->
find
RecordsWithRelations
(
$query
);
return
$this
->
find
WithRelations
(
$query
,
$returnScalar
);
}
if
(
$query
->
sql
!==
null
)
{
$sql
=
$query
->
sql
;
}
else
{
if
(
$query
->
from
===
null
)
{
$modelClass
=
$query
->
modelClass
;
$tableName
=
$modelClass
::
tableName
();
if
(
$query
->
from
===
null
)
{
if
(
$query
->
tableAlias
!==
null
)
{
$tableName
.=
' '
.
$query
->
tableAlias
;
}
...
...
@@ -83,46 +57,88 @@ class ActiveFinder extends \yii\base\Object
}
$this
->
applyScopes
(
$query
);
$sql
=
$this
->
connection
->
getQueryBuilder
()
->
build
(
$query
);
$prefix
=
$this
->
connection
->
quoteTableName
(
'@'
,
true
)
.
'.'
;
if
(
strpos
(
$sql
,
$prefix
)
!==
false
)
{
if
(
$query
->
tableAlias
!==
null
)
{
$alias
=
$this
->
connection
->
quoteTableName
(
$query
->
tableAlias
)
.
'.'
;
}
else
{
$class
=
$query
->
modelClass
;
$alias
=
$this
->
connection
->
quoteTableName
(
$class
::
tableName
())
.
'.'
;
}
$sql
=
str_replace
(
$prefix
,
$alias
,
$sql
);
$alias
=
$this
->
connection
->
quoteTableName
(
$tableName
)
.
'.'
;
}
$tokens
=
array
(
'@.'
=>
$alias
,
$this
->
connection
->
quoteTableName
(
'@'
,
true
)
.
'.'
=>
$alias
,
);
$sql
=
strtr
(
$sql
,
$tokens
);
}
$command
=
$this
->
connection
->
createCommand
(
$sql
,
$query
->
params
);
if
(
$returnScalar
)
{
return
$command
->
queryScalar
();
}
else
{
$rows
=
$command
->
queryAll
();
return
$this
->
createRecords
(
$query
,
$rows
);
}
}
protected
function
createRecords
(
$query
,
$rows
)
private
$_joinCount
;
private
$_tableAliases
;
private
$_hasMany
;
/**
* @param ActiveQuery $query
* @return array
*/
protected
function
findWithRelations
(
$query
,
$returnScalar
=
false
)
{
$records
=
array
();
if
(
$query
->
asArray
)
{
if
(
$query
->
index
===
null
)
{
return
$rows
;
$this
->
_joinCount
=
0
;
$this
->
_tableAliases
=
array
();
$this
->
_hasMany
=
false
;
$joinTree
=
new
JoinElement
(
$this
->
_joinCount
++
,
$query
,
null
,
null
);
if
(
$query
->
sql
!==
null
)
{
$command
=
$this
->
connection
->
createCommand
(
$query
->
sql
,
$query
->
params
);
if
(
$returnScalar
)
{
return
$command
->
queryScalar
();
}
foreach
(
$rows
as
$row
)
{
$records
[
$row
[
$query
->
index
]]
=
$row
;
$rows
=
$command
->
queryAll
();
$records
=
$this
->
createRecords
(
$query
,
$rows
);
$modelClass
=
$query
->
modelClass
;
$table
=
$modelClass
::
getMetaData
()
->
table
;
foreach
(
$records
as
$record
)
{
$pk
=
array
();
foreach
(
$table
->
primaryKey
as
$name
)
{
$pk
[]
=
$record
[
$name
];
}
}
else
{
$class
=
$query
->
modelClass
;
if
(
$query
->
index
===
null
)
{
foreach
(
$rows
as
$row
)
{
$records
[]
=
$class
::
create
(
$row
);
$pk
=
count
(
$pk
)
===
1
?
$pk
[
0
]
:
serialize
(
$pk
);
$joinTree
->
records
[
$pk
]
=
$record
;
}
}
else
{
foreach
(
$rows
as
$row
)
{
$records
[
$row
[
$query
->
index
]]
=
$class
::
create
(
$row
);
$q
=
new
ActiveQuery
(
$modelClass
);
$q
->
with
=
$query
->
with
;
$q
->
tableAlias
=
't'
;
$q
->
asArray
=
$query
->
asArray
;
$q
->
index
=
$query
->
index
;
$q
->
select
=
$table
->
primaryKey
;
$this
->
addPkCondition
(
$q
,
$table
,
$rows
,
't.'
);
$joinTree
->
query
=
$query
=
$q
;
}
$this
->
buildJoinTree
(
$joinTree
,
$query
->
with
);
$this
->
initJoinTree
(
$joinTree
);
$q
=
new
Query
;
$this
->
buildJoinQuery
(
$joinTree
,
$q
,
$returnScalar
);
if
(
$returnScalar
)
{
return
$q
->
createCommand
(
$this
->
connection
)
->
queryScalar
();
}
else
{
if
(
$this
->
_hasMany
&&
(
$query
->
limit
>
0
||
$query
->
offset
>
0
))
{
$this
->
limitQuery
(
$query
,
$q
);
}
$command
=
$q
->
createCommand
(
$this
->
connection
);
$rows
=
$command
->
queryAll
();
$joinTree
->
populateData
(
$rows
);
return
$query
->
index
===
null
?
array_values
(
$joinTree
->
records
)
:
$joinTree
->
records
;
}
return
$records
;
}
/**
...
...
@@ -130,7 +146,7 @@ class ActiveFinder extends \yii\base\Object
* @param ActiveRelation $relation
* @return array
*/
public
function
find
RelatedRecords
(
$record
,
$relation
)
public
function
find
WithRecord
(
$record
,
$relation
)
{
$this
->
_joinCount
=
0
;
$this
->
_tableAliases
=
array
();
...
...
@@ -169,69 +185,33 @@ class ActiveFinder extends \yii\base\Object
}
}
private
$_joinCount
;
private
$_tableAliases
;
private
$_hasMany
;
/**
* @param ActiveQuery $query
* @return array
*/
public
function
findRecordsWithRelations
(
$query
)
protected
function
createRecords
(
$query
,
$rows
)
{
if
(
$query
->
sql
!==
null
)
{
$command
=
$this
->
connection
->
createCommand
(
$query
->
sql
,
$query
->
params
);
$rows
=
$command
->
queryAll
();
$records
=
$this
->
createRecords
(
$query
,
$rows
);
$q
=
new
ActiveQuery
(
$query
->
modelClass
);
$q
->
with
=
$query
->
with
;
$q
->
tableAlias
=
't'
;
$q
->
asArray
=
$query
->
asArray
;
$q
->
index
=
$query
->
index
;
$modelClass
=
$query
->
modelClass
;
$table
=
$modelClass
::
getMetaData
()
->
table
;
$q
->
select
=
$table
->
primaryKey
;
$this
->
addPkCondition
(
$q
,
$table
,
$rows
,
't.'
);
$query
=
$q
;
$records
=
array
();
if
(
$query
->
asArray
)
{
if
(
$query
->
index
===
null
)
{
return
$rows
;
}
$this
->
_joinCount
=
0
;
$this
->
_tableAliases
=
array
();
$this
->
_hasMany
=
false
;
$joinTree
=
new
JoinElement
(
$this
->
_joinCount
++
,
$query
,
null
,
null
);
if
(
isset
(
$records
))
{
foreach
(
$records
as
$record
)
{
$pk
=
array
();
foreach
(
$table
->
primaryKey
as
$name
)
{
$pk
[]
=
$record
[
$name
];
foreach
(
$rows
as
$row
)
{
$records
[
$row
[
$query
->
index
]]
=
$row
;
}
$pk
=
count
(
$pk
)
===
1
?
$pk
[
0
]
:
serialize
(
$pk
);
$joinTree
->
records
[
$pk
]
=
$record
;
}
else
{
$class
=
$query
->
modelClass
;
if
(
$query
->
index
===
null
)
{
foreach
(
$rows
as
$row
)
{
$records
[]
=
$class
::
create
(
$row
);
}
}
else
{
foreach
(
$rows
as
$row
)
{
$records
[
$row
[
$query
->
index
]]
=
$class
::
create
(
$row
);
}
$this
->
buildJoinTree
(
$joinTree
,
$query
->
with
);
$this
->
initJoinTree
(
$joinTree
,
!
isset
(
$records
));
$q
=
new
Query
;
$this
->
buildJoinQuery
(
$joinTree
,
$q
);
if
(
$this
->
_hasMany
&&
(
$query
->
limit
>
0
||
$query
->
offset
>
0
))
{
$this
->
limitQuery
(
$query
,
$q
);
}
$rows
=
$q
->
createCommand
(
$this
->
connection
)
->
queryAll
();
$joinTree
->
populateData
(
$rows
);
return
$query
->
index
===
null
?
array_values
(
$joinTree
->
records
)
:
$joinTree
->
records
;
}
return
$records
;
}
protected
function
applyScopes
(
$query
)
{
if
(
$query
->
modelClass
===
null
||
$query
instanceof
ActiveQuery
&&
$query
->
sql
!==
null
)
{
return
;
}
$class
=
$query
->
modelClass
;
$class
::
defaultScope
(
$query
);
if
(
is_array
(
$query
->
scopes
))
{
...
...
@@ -281,7 +261,6 @@ class ActiveFinder extends \yii\base\Object
if
(
isset
(
$parent
->
children
[
$with
]))
{
$child
=
$parent
->
children
[
$with
];
$child
->
joinOnly
=
false
;
}
else
{
$modelClass
=
$parent
->
query
->
modelClass
;
$relations
=
$modelClass
::
getMetaData
()
->
relations
;
...
...
@@ -292,8 +271,9 @@ class ActiveFinder extends \yii\base\Object
if
(
is_string
(
$relation
->
via
))
{
// join via an existing relation
$parent2
=
$this
->
buildJoinTree
(
$parent
,
$relation
->
via
);
if
(
$parent2
->
joinOnly
===
null
)
{
$parent2
->
joinOnly
=
true
;
if
(
$parent2
->
query
->
select
===
null
)
{
$parent2
->
query
->
select
=
false
;
unset
(
$parent2
->
container
->
relations
[
$parent2
->
query
->
name
]);
}
$child
=
new
JoinElement
(
$this
->
_joinCount
++
,
$relation
,
$parent2
,
$parent
);
}
elseif
(
is_array
(
$relation
->
via
))
{
...
...
@@ -312,7 +292,6 @@ class ActiveFinder extends \yii\base\Object
}
$parent2
=
new
JoinElement
(
$this
->
_joinCount
++
,
$r
,
$parent
,
$parent
);
$parent2
->
joinOnly
=
true
;
$child
=
new
JoinElement
(
$this
->
_joinCount
++
,
$relation
,
$parent2
,
$parent
);
}
else
{
...
...
@@ -329,9 +308,8 @@ class ActiveFinder extends \yii\base\Object
/**
* @param JoinElement $element
* @param boolean $applyScopes
*/
protected
function
initJoinTree
(
$element
,
$applyScopes
=
true
)
protected
function
initJoinTree
(
$element
)
{
if
(
$element
->
query
->
tableAlias
!==
null
)
{
$alias
=
$element
->
query
->
tableAlias
;
...
...
@@ -355,7 +333,7 @@ class ActiveFinder extends \yii\base\Object
$this
->
_tableAliases
[
$alias
]
=
true
;
$element
->
query
->
tableAlias
=
$alias
;
if
(
$
applyScopes
)
{
if
(
$
element
->
records
!==
array
()
)
{
$this
->
applyScopes
(
$element
->
query
);
}
...
...
@@ -364,7 +342,7 @@ class ActiveFinder extends \yii\base\Object
}
foreach
(
$element
->
children
as
$child
)
{
$this
->
initJoinTree
(
$child
,
$count
);
$this
->
initJoinTree
(
$child
);
}
}
...
...
@@ -372,7 +350,7 @@ class ActiveFinder extends \yii\base\Object
* @param JoinElement $element
* @param \yii\db\dao\Query $query
*/
protected
function
buildJoinQuery
(
$element
,
$query
)
protected
function
buildJoinQuery
(
$element
,
$query
,
$keepSelect
=
false
)
{
if
(
$element
->
parent
)
{
$prefixes
=
array
(
...
...
@@ -396,9 +374,21 @@ class ActiveFinder extends \yii\base\Object
$qb
=
$this
->
connection
->
getQueryBuilder
();
if
(
$keepSelect
)
{
if
(
!
empty
(
$element
->
query
->
select
))
{
$select
=
$element
->
query
->
select
;
if
(
is_string
(
$select
))
{
$select
=
explode
(
','
,
$select
);
}
foreach
(
$select
as
$column
)
{
$query
->
select
[]
=
strtr
(
trim
(
$column
),
$prefixes
);
}
}
}
else
{
foreach
(
$this
->
buildSelect
(
$element
,
$element
->
query
->
select
)
as
$column
)
{
$query
->
select
[]
=
strtr
(
$column
,
$prefixes
);
}
}
if
(
$element
->
query
instanceof
ActiveQuery
)
{
if
(
$element
->
query
->
from
===
null
)
{
...
...
@@ -492,7 +482,7 @@ class ActiveFinder extends \yii\base\Object
}
foreach
(
$element
->
children
as
$child
)
{
$this
->
buildJoinQuery
(
$child
,
$query
);
$this
->
buildJoinQuery
(
$child
,
$query
,
$keepSelect
);
}
}
...
...
@@ -534,7 +524,11 @@ class ActiveFinder extends \yii\base\Object
$columns
[]
=
$column
;
}
elseif
(
!
isset
(
$element
->
pkAlias
[
$column
]))
{
$alias
=
"c
{
$element
->
id
}
_"
.
(
$columnCount
++
);
if
(
strpos
(
$column
,
'('
)
!==
false
)
{
$columns
[]
=
"
$column
AS
$alias
"
;
}
else
{
$columns
[]
=
"
$prefix
.
$column
AS
$alias
"
;
}
$element
->
columnAliases
[
$alias
]
=
$column
;
}
}
...
...
framework/db/ar/ActiveQuery.php
View file @
ed513b4d
...
...
@@ -18,20 +18,6 @@ use yii\db\Exception;
* 1. eager loading, base limited and has has_many relations
* 2.
* ActiveFinder.php is ...
* todo: add SQL monitor
* todo: better handling on join() support in QueryBuilder: use regexp to detect table name and quote it
* todo: do not support anonymous parameter binding
* todo: quote join/on part of the relational query
* todo: modify QueryBuilder about join() methods
* todo: unify ActiveFinder and ActiveRelation in query building process
* todo: intelligent table aliasing (first table name, then relation name, finally t?)
* todo: allow using tokens in primary query fragments
* todo: findBySql
* todo: base limited
* todo: lazy loading
* todo: scope
* todo: test via option
* todo: count, sum, exists
*
* @property integer $count
*
...
...
@@ -97,15 +83,21 @@ class ActiveQuery extends BaseActiveQuery implements \IteratorAggregate, \ArrayA
return
isset
(
$this
->
records
[
0
])
?
$this
->
records
[
0
]
:
null
;
}
/**
* Returns a scalar value for this query.
* The value returned will be the first column in the first row of the query results.
* @return string|boolean the value of the first column in the first row of the query result.
* False is returned if there is no value.
*/
public
function
value
()
{
$
result
=
$this
->
asArray
()
->
one
(
);
return
$
result
===
null
?
null
:
reset
(
$result
);
$
finder
=
new
ActiveFinder
(
$this
->
getDbConnection
()
);
return
$
finder
->
find
(
$this
,
true
);
}
public
function
exists
()
{
return
$this
->
select
(
array
(
new
Expression
(
'1'
)))
->
asArray
()
->
one
()
!==
null
;
return
$this
->
select
(
array
(
new
Expression
(
'1'
)))
->
value
()
!==
false
;
}
/**
...
...
@@ -243,6 +235,6 @@ class ActiveQuery extends BaseActiveQuery implements \IteratorAggregate, \ArrayA
protected
function
findRecords
()
{
$finder
=
new
ActiveFinder
(
$this
->
getDbConnection
());
return
$finder
->
find
Records
(
$this
);
return
$finder
->
find
(
$this
);
}
}
framework/db/ar/ActiveRecord.php
View file @
ed513b4d
...
...
@@ -134,13 +134,17 @@ abstract class ActiveRecord extends Model
*
* ~~~
* // count the total number of customers
* echo Customer::count();
* // count the number of customers whose primary key value is 10.
* echo Customer::count(10);
* echo Customer::count()->value();
* // count the number of active customers:
* echo Customer::count(array(
* 'where' => array('status' => 1),
* ));
* ))->value();
* // equivalent usage:
* echo Customer::count()
* ->where(array('status' => 1))
* ->value();
* // customize the count option
* echo Customer::count('COUNT(DISTINCT age)')->value();
* ~~~
*
* @param mixed $q the query parameter. This can be one of the followings:
...
...
@@ -157,13 +161,9 @@ abstract class ActiveRecord extends Model
foreach
(
$q
as
$name
=>
$value
)
{
$query
->
$name
=
$value
;
}
}
elseif
(
$q
!==
null
)
{
// query by primary key
$primaryKey
=
static
::
getMetaData
()
->
table
->
primaryKey
;
$query
->
where
(
array
(
$primaryKey
[
0
]
=>
$q
));
}
if
(
$query
->
select
===
null
)
{
$query
->
select
=
'COUNT(*)'
;
$query
->
select
=
array
(
'COUNT(*)'
)
;
}
return
$query
->
value
();
}
...
...
@@ -561,7 +561,7 @@ abstract class ActiveRecord extends Model
}
$finder
=
new
ActiveFinder
(
$this
->
getDbConnection
());
return
$finder
->
find
RelatedRecords
(
$this
,
$relation
);
return
$finder
->
find
WithRecord
(
$this
,
$relation
);
}
/**
...
...
framework/db/ar/JoinElement.php
View file @
ed513b4d
...
...
@@ -39,10 +39,6 @@ class JoinElement extends \yii\base\Object
*/
public
$relations
=
array
();
/**
* @var boolean whether this element is only for join purpose. If false, data will be populated into the AR of this element.
*/
public
$joinOnly
;
/**
* @var array column aliases (alias => original name)
*/
public
$columnAliases
=
array
();
...
...
tests/unit/framework/db/ar/ActiveRecordTest.php
View file @
ed513b4d
...
...
@@ -225,18 +225,21 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase
public
function
testEagerLoading
()
{
// has many
$customers
=
Customer
::
find
()
->
with
(
'orders'
)
->
order
(
'@.id'
)
->
all
();
$this
->
assertEquals
(
3
,
count
(
$customers
));
$this
->
assertEquals
(
1
,
count
(
$customers
[
0
]
->
orders
));
$this
->
assertEquals
(
2
,
count
(
$customers
[
1
]
->
orders
));
$this
->
assertEquals
(
0
,
count
(
$customers
[
2
]
->
orders
));
// nested
$customers
=
Customer
::
find
()
->
with
(
'orders.customer'
)
->
order
(
'@.id'
)
->
all
();
$this
->
assertEquals
(
3
,
count
(
$customers
));
$this
->
assertEquals
(
1
,
$customers
[
0
]
->
orders
[
0
]
->
customer
->
id
);
$this
->
assertEquals
(
2
,
$customers
[
1
]
->
orders
[
0
]
->
customer
->
id
);
$this
->
assertEquals
(
2
,
$customers
[
1
]
->
orders
[
1
]
->
customer
->
id
);
// has many via relation
$orders
=
Order
::
find
()
->
with
(
'items'
)
->
order
(
'@.id'
)
->
all
();
$this
->
assertEquals
(
3
,
count
(
$orders
));
$this
->
assertEquals
(
1
,
$orders
[
0
]
->
items
[
0
]
->
id
);
...
...
@@ -245,18 +248,22 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase
$this
->
assertEquals
(
4
,
$orders
[
1
]
->
items
[
1
]
->
id
);
$this
->
assertEquals
(
5
,
$orders
[
1
]
->
items
[
2
]
->
id
);
// has many via join table
$orders
=
Order
::
find
()
->
with
(
'books'
)
->
order
(
'@.id'
)
->
all
();
$this
->
assertEquals
(
2
,
count
(
$orders
));
$this
->
assertEquals
(
1
,
$orders
[
0
]
->
books
[
0
]
->
id
);
$this
->
assertEquals
(
2
,
$orders
[
0
]
->
books
[
1
]
->
id
);
$this
->
assertEquals
(
2
,
$orders
[
1
]
->
books
[
0
]
->
id
);
// has many and base limited
$orders
=
Order
::
find
()
->
with
(
'items'
)
->
order
(
'@.id'
)
->
limit
(
2
)
->
all
();
$this
->
assertEquals
(
2
,
count
(
$orders
));
// findBySql with
$orders
=
Order
::
findBySql
(
'SELECT * FROM tbl_order WHERE customer_id=2'
)
->
with
(
'items'
)
->
all
();
$this
->
assertEquals
(
2
,
count
(
$orders
));
// index and array
$customers
=
Customer
::
find
()
->
with
(
'orders.customer'
)
->
order
(
'@.id'
)
->
index
(
'id'
)
->
asArray
()
->
all
();
$this
->
assertEquals
(
3
,
count
(
$customers
));
$this
->
assertTrue
(
isset
(
$customers
[
1
],
$customers
[
2
],
$customers
[
3
]));
...
...
@@ -265,6 +272,15 @@ class ActiveRecordTest extends \yiiunit\MysqlTestCase
$this
->
assertEquals
(
2
,
count
(
$customers
[
2
][
'orders'
]));
$this
->
assertEquals
(
0
,
count
(
$customers
[
3
][
'orders'
]));
$this
->
assertTrue
(
is_array
(
$customers
[
1
][
'orders'
][
0
][
'customer'
]));
// count with
$this
->
assertEquals
(
3
,
Order
::
count
());
$value
=
Order
::
count
(
array
(
'select'
=>
array
(
'COUNT(DISTINCT @.id, @.customer_id)'
),
'with'
=>
'books'
,
));
$this
->
assertEquals
(
2
,
$value
);
}
public
function
testLazyLoading
()
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment