Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
syncEnrollments
Project
Project
Details
Activity
Releases
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
Yeray Santana Hualde
syncEnrollments
Commits
badc8a60
Commit
badc8a60
authored
Aug 01, 2011
by
Taylor Otwell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
database refactoring.
parent
829088f3
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
522 additions
and
387 deletions
+522
-387
aliases.php
application/config/aliases.php
+2
-2
index.php
public/index.php
+13
-1
connection.php
system/db/connection.php
+138
-0
connector.php
system/db/connector.php
+20
-35
hydrator.php
system/db/eloquent/hydrator.php
+13
-24
model.php
system/db/eloquent/model.php
+96
-88
manager.php
system/db/manager.php
+65
-0
query.php
system/db/query.php
+175
-97
compiler.php
system/db/query/compiler.php
+0
-86
dynamic.php
system/db/query/dynamic.php
+0
-54
No files found.
application/config/aliases.php
View file @
badc8a60
...
...
@@ -26,8 +26,8 @@ return array(
'Cookie'
=>
'System\\Cookie'
,
'Crypt'
=>
'System\\Crypt'
,
'Date'
=>
'System\\Date'
,
'DB'
=>
'System\\DB'
,
'Eloquent'
=>
'System\\DB\\Eloquent'
,
'DB'
=>
'System\\DB
\\Manager
'
,
'Eloquent'
=>
'System\\DB\\Eloquent
\\Model
'
,
'File'
=>
'System\\File'
,
'Form'
=>
'System\\Form'
,
'Hash'
=>
'System\\Hash'
,
...
...
public/index.php
View file @
badc8a60
...
...
@@ -8,6 +8,8 @@
* @link http://laravel.com
*/
$t
=
microtime
(
true
);
// --------------------------------------------------------------
// The path to the application directory.
// --------------------------------------------------------------
...
...
@@ -135,6 +137,16 @@ if (System\Config::get('session.driver') != '')
System\Session
::
load
(
System\Cookie
::
get
(
'laravel_session'
));
}
// --------------------------------------------------------------
// Load all of the core routing classes.
// --------------------------------------------------------------
require
SYS_PATH
.
'request'
.
EXT
;
require
SYS_PATH
.
'response'
.
EXT
;
require
SYS_PATH
.
'routing/route'
.
EXT
;
require
SYS_PATH
.
'routing/router'
.
EXT
;
require
SYS_PATH
.
'routing/loader'
.
EXT
;
require
SYS_PATH
.
'routing/filter'
.
EXT
;
// --------------------------------------------------------------
// Register the route filters.
// --------------------------------------------------------------
...
...
@@ -150,7 +162,7 @@ $response = System\Routing\Filter::call('before', array(), true);
// ----------------------------------------------------------
if
(
is_null
(
$response
))
{
$route
=
System\Routing\Router
::
make
(
Request
::
method
(),
Request
::
uri
(),
new
System\Routing\Loader
)
->
route
();
$route
=
System\Routing\Router
::
make
(
System\Request
::
method
(),
System\Request
::
uri
(),
new
System\Routing\Loader
(
APP_PATH
)
)
->
route
();
$response
=
(
is_null
(
$route
))
?
System\Response
::
error
(
'404'
)
:
$route
->
call
();
}
...
...
system/db.php
→
system/db
/connection
.php
View file @
badc8a60
<?php
namespace
System
;
<?php
namespace
System
\DB
;
class
DB
{
class
Connection
{
/**
* The
established database connections
.
* The
connection name
.
*
* @var
array
* @var
string
*/
public
static
$connections
=
array
()
;
public
$name
;
/**
* Get a database connection. If no database name is specified, the default
* connection will be returned as defined in the db configuration file.
* The connection configuration.
*
* Note: Database connections are managed as singletons.
* @var array
*/
public
$config
;
/**
* The Connector instance.
*
* @param string $connection
* @return PDO
* @var Connector
*/
public
static
function
connection
(
$connection
=
null
)
{
if
(
is_null
(
$connection
))
{
$connection
=
Config
::
get
(
'db.default'
);
}
public
$connector
;
if
(
!
array_key_exists
(
$connection
,
static
::
$connections
))
{
static
::
$connections
[
$connection
]
=
DB\Connector
::
connect
(
$connection
);
}
/**
* The PDO connection.
*
* @var PDO
*/
public
$pdo
;
return
static
::
$connections
[
$connection
];
/**
* Create a new Connection instance.
*
* @param string $name
* @param object $config
* @param Conector $connector
* @return void
*/
public
function
__construct
(
$name
,
$config
,
$connector
)
{
$this
->
name
=
$name
;
$this
->
config
=
$config
;
$this
->
connector
=
$connector
;
$this
->
pdo
=
$this
->
connector
->
connect
(
$this
->
config
);
}
/**
...
...
@@ -38,12 +51,11 @@ class DB {
*
* @param string $sql
* @param array $bindings
* @param string $connection
* @return object
*/
public
static
function
first
(
$sql
,
$bindings
=
array
(),
$connection
=
null
)
public
function
first
(
$sql
,
$bindings
=
array
()
)
{
return
(
count
(
$results
=
static
::
query
(
$sql
,
$bindings
,
$connection
))
>
0
)
?
$results
[
0
]
:
null
;
return
(
count
(
$results
=
$this
->
query
(
$sql
,
$bindings
))
>
0
)
?
$results
[
0
]
:
null
;
}
/**
...
...
@@ -58,12 +70,11 @@ class DB {
*
* @param string $sql
* @param array $bindings
* @param string $connection
* @return array
*/
public
static
function
query
(
$sql
,
$bindings
=
array
(),
$connection
=
null
)
public
function
query
(
$sql
,
$bindings
=
array
()
)
{
$query
=
static
::
connection
(
$connection
)
->
prepare
(
$sql
);
$query
=
$this
->
pdo
->
prepare
(
$sql
);
$result
=
$query
->
execute
(
$bindings
);
...
...
@@ -84,44 +95,44 @@ class DB {
/**
* Begin a fluent query against a table.
*
* This method is simply a convenient shortcut into Query::table.
*
* @param string $table
* @param string $connection
* @return Query
*/
public
static
function
table
(
$table
,
$connection
=
null
)
public
function
table
(
$table
)
{
return
new
DB\Query
(
$table
,
$connection
);
return
new
Query
(
$table
,
$this
);
}
/**
* Get the
driver name for a databas
e connection.
* Get the
keyword identifier wrapper for th
e connection.
*
* @param string $connection
* @return string
*/
public
static
function
driver
(
$connection
=
null
)
public
function
wrapper
(
)
{
return
static
::
connection
(
$connection
)
->
getAttribute
(
\PDO
::
ATTR_DRIVER_NAME
);
if
(
array_key_exists
(
'wrap'
,
$this
->
config
)
and
$this
->
config
[
'wrap'
]
===
false
)
return
''
;
return
(
$this
->
driver
()
==
'mysql'
)
?
'`'
:
'"'
;
}
/**
* Get the
table prefix for a
database connection.
* Get the
driver name for the
database connection.
*
* @param string $connection
* @return string
*/
public
static
function
prefix
(
$connection
=
null
)
public
function
driver
(
)
{
$connections
=
Config
::
get
(
'db.connections'
);
if
(
is_null
(
$connection
))
{
$connection
=
Config
::
get
(
'db.default'
);
}
return
$this
->
pdo
->
getAttribute
(
\PDO
::
ATTR_DRIVER_NAME
);
}
return
(
array_key_exists
(
'prefix'
,
$connections
[
$connection
]))
?
$connections
[
$connection
][
'prefix'
]
:
''
;
/**
* Get the table prefix for the database connection.
*
* @return string
*/
public
function
prefix
()
{
return
(
array_key_exists
(
'prefix'
,
$this
->
config
))
?
$this
->
config
[
'prefix'
]
:
''
;
}
}
\ No newline at end of file
system/db/connector.php
View file @
badc8a60
...
...
@@ -9,7 +9,7 @@ class Connector {
*
* @var array
*/
public
static
$options
=
array
(
public
$options
=
array
(
\PDO
::
ATTR_CASE
=>
\PDO
::
CASE_LOWER
,
\PDO
::
ATTR_ERRMODE
=>
\PDO
::
ERRMODE_EXCEPTION
,
\PDO
::
ATTR_ORACLE_NULLS
=>
\PDO
::
NULL_NATURAL
,
...
...
@@ -20,24 +20,22 @@ class Connector {
/**
* Establish a PDO database connection.
*
* @param
string
$connection
* @param
object
$connection
* @return PDO
*/
public
static
function
connect
(
$connection
)
public
function
connect
(
$config
)
{
$config
=
static
::
configuration
(
$connection
);
switch
(
$config
->
driver
)
{
case
'sqlite'
:
return
static
::
connect_to_sqlite
(
$config
);
return
$this
->
connect_to_sqlite
(
$config
);
case
'mysql'
:
case
'pgsql'
:
return
static
::
connect_to_server
(
$config
);
return
$this
->
connect_to_server
(
$config
);
default
:
return
static
::
connect_to_generic
(
$config
);
return
$this
->
connect_to_generic
(
$config
);
}
throw
new
\Exception
(
'Database driver '
.
$config
->
driver
.
' is not supported.'
);
...
...
@@ -47,20 +45,25 @@ class Connector {
* Establish a PDO connection to a SQLite database.
*
* SQLite database paths can be specified either relative to the application/db
* directory, or as an absolute path to any location on the file system.
* directory, or as an absolute path to any location on the file system. In-memory
* databases are also supported.
*
* @param object $config
* @return PDO
*/
private
static
function
connect_to_sqlite
(
$config
)
private
function
connect_to_sqlite
(
$config
)
{
if
(
file_exists
(
$path
=
DATABASE_PATH
.
$config
->
database
.
'.sqlite'
))
if
(
$config
->
database
==
':memory:'
)
{
return
new
\PDO
(
'sqlite::memory:'
,
null
,
null
,
$this
->
options
);
}
elseif
(
file_exists
(
$path
=
DATABASE_PATH
.
$config
->
database
.
'.sqlite'
))
{
return
new
\PDO
(
'sqlite:'
.
$path
,
null
,
null
,
static
::
$
options
);
return
new
\PDO
(
'sqlite:'
.
$path
,
null
,
null
,
$this
->
options
);
}
elseif
(
file_exists
(
$config
->
database
))
{
return
new
\PDO
(
'sqlite:'
.
$config
->
database
,
null
,
null
,
static
::
$
options
);
return
new
\PDO
(
'sqlite:'
.
$config
->
database
,
null
,
null
,
$this
->
options
);
}
else
{
...
...
@@ -74,7 +77,7 @@ class Connector {
* @param object $config
* @return PDO
*/
private
static
function
connect_to_server
(
$config
)
private
function
connect_to_server
(
$config
)
{
$dsn
=
$config
->
driver
.
':host='
.
$config
->
host
.
';dbname='
.
$config
->
database
;
...
...
@@ -83,7 +86,7 @@ class Connector {
$dsn
.=
';port='
.
$config
->
port
;
}
$connection
=
new
\PDO
(
$dsn
,
$config
->
username
,
$config
->
password
,
static
::
$
options
);
$connection
=
new
\PDO
(
$dsn
,
$config
->
username
,
$config
->
password
,
$this
->
options
);
if
(
isset
(
$config
->
charset
))
{
...
...
@@ -99,27 +102,9 @@ class Connector {
* @param object $config
* @return PDO
*/
private
static
function
connect_to_generic
(
$config
)
private
function
connect_to_generic
(
$config
)
{
return
new
\PDO
(
$config
->
driver
.
':'
.
$config
->
dsn
,
$config
->
username
,
$config
->
password
,
static
::
$options
);
}
/**
* Get the configuration options for a database connection.
*
* @param string $connection
* @return object
*/
private
static
function
configuration
(
$connection
)
{
$config
=
Config
::
get
(
'db.connections'
);
if
(
!
array_key_exists
(
$connection
,
$config
))
{
throw
new
\Exception
(
"Database connection [
$connection
] is not defined."
);
}
return
(
object
)
$config
[
$connection
];
return
new
\PDO
(
$config
->
driver
.
':'
.
$config
->
dsn
,
$config
->
username
,
$config
->
password
,
$this
->
options
);
}
}
\ No newline at end of file
system/db/eloquent/hydrator.php
View file @
badc8a60
<?php
namespace
System\DB\Eloquent
;
use
System\DB\Eloquent
;
class
Hydrator
{
/**
* Load the array of hydrated models and their eager relationships.
*
* @param
object
$eloquent
* @param
Model
$eloquent
* @return array
*/
public
static
function
hydrate
(
$eloquent
)
...
...
@@ -68,8 +66,6 @@ class Hydrator {
*/
private
static
function
eagerly
(
$eloquent
,
&
$parents
,
$include
)
{
// Get the relationship Eloquent model.
//
// We temporarily spoof the belongs_to key to allow the query to be fetched without
// any problems, since the belongs_to method actually gets the attribute.
$eloquent
->
attributes
[
$spoof
=
$include
.
'_id'
]
=
0
;
...
...
@@ -79,9 +75,7 @@ class Hydrator {
unset
(
$eloquent
->
attributes
[
$spoof
]);
// Reset the WHERE clause and bindings on the query. We'll add our own WHERE clause soon.
$relationship
->
query
->
where
=
'WHERE 1 = 1'
;
$relationship
->
query
->
bindings
=
array
();
$relationship
->
query
->
reset_where
();
// Initialize the relationship attribute on the parents. As expected, "many" relationships
// are initialized to an array and "one" relationships are initialized to null.
...
...
@@ -90,21 +84,13 @@ class Hydrator {
$parent
->
ignore
[
$include
]
=
(
in_array
(
$eloquent
->
relating
,
array
(
'has_many'
,
'has_and_belongs_to_many'
)))
?
array
()
:
null
;
}
if
(
$eloquent
->
relating
==
'has_one'
)
{
static
::
eagerly_load_one
(
$relationship
,
$parents
,
$eloquent
->
relating_key
,
$include
);
}
elseif
(
$eloquent
->
relating
==
'has_many'
)
{
static
::
eagerly_load_many
(
$relationship
,
$parents
,
$eloquent
->
relating_key
,
$include
);
}
elseif
(
$eloquent
->
relating
==
'belongs_to'
)
if
(
in_array
(
$relating
=
$eloquent
->
relating
,
array
(
'has_one'
,
'has_many'
,
'belongs_to'
)))
{
static
::
eagerly_load_belonging
(
$relationship
,
$parents
,
$eloquent
->
relating_key
,
$include
);
return
static
::
$relating
(
$relationship
,
$parents
,
$eloquent
->
relating_key
,
$include
);
}
else
{
static
::
eagerly_load_many
_to_many
(
$relationship
,
$parents
,
$eloquent
->
relating_key
,
$eloquent
->
relating_table
,
$include
);
static
::
has_and_belongs
_to_many
(
$relationship
,
$parents
,
$eloquent
->
relating_key
,
$eloquent
->
relating_table
,
$include
);
}
}
...
...
@@ -118,7 +104,7 @@ class Hydrator {
* @param string $include
* @return void
*/
private
static
function
eagerly_load
_one
(
$relationship
,
&
$parents
,
$relating_key
,
$include
)
private
static
function
has
_one
(
$relationship
,
&
$parents
,
$relating_key
,
$include
)
{
foreach
(
$relationship
->
where_in
(
$relating_key
,
array_keys
(
$parents
))
->
get
()
as
$key
=>
$child
)
{
...
...
@@ -136,7 +122,7 @@ class Hydrator {
* @param string $include
* @return void
*/
private
static
function
eagerly_load
_many
(
$relationship
,
&
$parents
,
$relating_key
,
$include
)
private
static
function
has
_many
(
$relationship
,
&
$parents
,
$relating_key
,
$include
)
{
foreach
(
$relationship
->
where_in
(
$relating_key
,
array_keys
(
$parents
))
->
get
()
as
$key
=>
$child
)
{
...
...
@@ -153,7 +139,7 @@ class Hydrator {
* @param string $include
* @return void
*/
private
static
function
eagerly_load_belonging
(
$relationship
,
&
$parents
,
$relating_key
,
$include
)
private
static
function
belongs_to
(
$relationship
,
&
$parents
,
$relating_key
,
$include
)
{
$keys
=
array
();
...
...
@@ -184,14 +170,17 @@ class Hydrator {
*
* @return void
*/
private
static
function
eagerly_load_many
_to_many
(
$relationship
,
&
$parents
,
$relating_key
,
$relating_table
,
$include
)
private
static
function
has_and_belongs
_to_many
(
$relationship
,
&
$parents
,
$relating_key
,
$relating_table
,
$include
)
{
// The model "has and belongs to many" method sets the SELECT clause; however, we need
// to clear it here since we will be adding the foreign key to the select.
$relationship
->
query
->
select
=
null
;
$relationship
->
query
->
where_in
(
$relating_table
.
'.'
.
$relating_key
,
array_keys
(
$parents
));
// The foreign key is added to the select to allow us to easily match the models back to their parents.
$children
=
$relationship
->
query
->
get
(
array
(
Eloquent
::
table
(
get_class
(
$relationship
))
.
'.*'
,
$relating_table
.
'.'
.
$relating_key
));
// Otherwise, there would be no apparent connection between the models to allow us to match them.
$children
=
$relationship
->
query
->
get
(
array
(
Model
::
table
(
get_class
(
$relationship
))
.
'.*'
,
$relating_table
.
'.'
.
$relating_key
));
$class
=
get_class
(
$relationship
);
...
...
system/db/eloquent.php
→
system/db/eloquent
/model
.php
View file @
badc8a60
<?php
namespace
System\DB
;
<?php
namespace
System\DB
\Eloquent
;
use
System\Str
;
use
System\Config
;
use
System\Inflector
;
use
System\DB\Manager
;
abstract
class
Eloquent
{
abstract
class
Model
{
/**
* The connection that shold be used for the model.
*
* @var string
*/
public
static
$connection
;
/**
* The model query instance.
*
* @var Query
*/
public
$query
;
/**
* Indicates if the model exists in the database.
...
...
@@ -69,13 +84,6 @@ abstract class Eloquent {
*/
public
$relating_table
;
/**
* The model query instance.
*
* @var Query
*/
public
$query
;
/**
* Create a new Eloquent model instance.
*
...
...
@@ -102,51 +110,57 @@ abstract class Eloquent {
}
/**
*
Get the table name for a
model.
*
Set the eagerly loaded models on the queryable
model.
*
* @param string $class
* @return string
* @return Model
*/
p
ublic
static
function
table
(
$class
)
p
rivate
function
_with
(
)
{
if
(
property_exists
(
$class
,
'table'
))
{
return
$class
::
$table
;
}
return
strtolower
(
Inflector
::
plural
(
$class
));
$this
->
includes
=
func_get_args
();
return
$this
;
}
/**
* Factory for creating
new
Eloquent model instances.
* Factory for creating
queryable
Eloquent model instances.
*
* @param string $class
* @return object
*/
public
static
function
make
(
$class
)
public
static
function
query
(
$class
)
{
$model
=
new
$class
;
// Since this method is only used for instantiating models for querying
// purposes, we will go ahead and set the Query instance on the model.
$model
->
query
=
Query
::
table
(
static
::
table
(
$class
));
$model
->
query
=
Manager
::
connection
(
static
::
$connection
)
->
table
(
static
::
table
(
$class
));
return
$model
;
}
/**
* Create a new model instance and set the relationships
* that should be eagerly loaded.
* Get the table name for a model.
*
* @return mixed
* @param string $class
* @return string
*/
public
static
function
with
(
)
public
static
function
table
(
$class
)
{
$model
=
static
::
make
(
get_called_class
());
if
(
property_exists
(
$class
,
'table'
))
{
return
$class
::
$table
;
}
$model
->
includes
=
func_get_args
();
return
strtolower
(
Inflector
::
plural
(
$class
));
}
return
$model
;
/**
* Get all of the models from the database.
*
* @return array
*/
public
static
function
all
()
{
return
Hydrator
::
hydrate
(
static
::
query
(
get_called_class
()));
}
/**
...
...
@@ -157,7 +171,7 @@ abstract class Eloquent {
*/
public
static
function
find
(
$id
)
{
return
static
::
make
(
get_called_class
())
->
where
(
'id'
,
'='
,
$id
)
->
first
();
return
static
::
query
(
get_called_class
())
->
where
(
'id'
,
'='
,
$id
)
->
first
();
}
/**
...
...
@@ -167,7 +181,7 @@ abstract class Eloquent {
*/
private
function
_get
()
{
return
Eloquent\
Hydrator
::
hydrate
(
$this
);
return
Hydrator
::
hydrate
(
$this
);
}
/**
...
...
@@ -177,7 +191,7 @@ abstract class Eloquent {
*/
private
function
_first
()
{
return
(
count
(
$results
=
Eloquent\
Hydrator
::
hydrate
(
$this
->
take
(
1
)))
>
0
)
?
reset
(
$results
)
:
null
;
return
(
count
(
$results
=
Hydrator
::
hydrate
(
$this
->
take
(
1
)))
>
0
)
?
reset
(
$results
)
:
null
;
}
/**
...
...
@@ -192,12 +206,7 @@ abstract class Eloquent {
if
(
is_null
(
$per_page
))
{
if
(
!
property_exists
(
get_class
(
$this
),
'per_page'
))
{
throw
new
\Exception
(
"The number of models to display per page for model ["
.
get_class
(
$this
)
.
"] has not been specified."
);
}
$per_page
=
static
::
$per_page
;
$per_page
=
(
property_exists
(
get_class
(
$this
),
'per_page'
))
?
static
::
$per_page
:
15
;
}
$current_page
=
\System\Paginator
::
page
(
$total
,
$per_page
);
...
...
@@ -248,7 +257,7 @@ abstract class Eloquent {
{
$this
->
relating_key
=
(
is_null
(
$foreign_key
))
?
strtolower
(
get_class
(
$this
))
.
'_id'
:
$foreign_key
;
return
static
::
make
(
$model
)
->
where
(
$this
->
relating_key
,
'='
,
$this
->
id
);
return
static
::
query
(
$model
)
->
where
(
$this
->
relating_key
,
'='
,
$this
->
id
);
}
/**
...
...
@@ -277,7 +286,7 @@ abstract class Eloquent {
$this
->
relating_key
=
$caller
[
'function'
]
.
'_id'
;
}
return
static
::
make
(
$model
)
->
where
(
'id'
,
'='
,
$this
->
attributes
[
$this
->
relating_key
]);
return
static
::
query
(
$model
)
->
where
(
'id'
,
'='
,
$this
->
attributes
[
$this
->
relating_key
]);
}
/**
...
...
@@ -318,7 +327,7 @@ abstract class Eloquent {
$associated_key
=
(
is_null
(
$associated_key
))
?
strtolower
(
$model
)
.
'_id'
:
$associated_key
;
return
static
::
make
(
$model
)
return
static
::
query
(
$model
)
->
select
(
array
(
static
::
table
(
$model
)
.
'.*'
))
->
join
(
$this
->
relating_table
,
static
::
table
(
$model
)
.
'.id'
,
'='
,
$this
->
relating_table
.
'.'
.
$associated_key
)
->
where
(
$this
->
relating_table
.
'.'
.
$this
->
relating_key
,
'='
,
$this
->
id
);
...
...
@@ -331,25 +340,19 @@ abstract class Eloquent {
*/
public
function
save
()
{
if
(
$this
->
exists
and
count
(
$this
->
dirty
)
==
0
)
{
return
true
;
}
// If the model does not have any dirty attributes, there is no reason
// to save it to the database.
if
(
$this
->
exists
and
count
(
$this
->
dirty
)
==
0
)
return
true
;
$model
=
get_class
(
$this
);
// Since the model was instantiated using "new", a query instance has not been set.
// Only models being used for querying have their query instances set by default.
$this
->
query
=
Query
::
table
(
static
::
table
(
$model
));
$this
->
query
=
Manager
::
connection
(
static
::
$connection
)
->
table
(
static
::
table
(
$model
));
if
(
property_exists
(
$model
,
'timestamps'
)
and
$model
::
$timestamps
)
{
$this
->
updated_at
=
date
(
'Y-m-d H:i:s'
);
if
(
!
$this
->
exists
)
{
$this
->
created_at
=
$this
->
updated_at
;
}
$this
->
timestamp
();
}
// If the model already exists in the database, we will just update it.
...
...
@@ -368,6 +371,21 @@ abstract class Eloquent {
$this
->
dirty
=
array
();
}
/**
* Set the creation and update timestamps on the model.
*
* @return void
*/
private
function
timestamp
()
{
$this
->
updated_at
=
date
(
'Y-m-d H:i:s'
);
if
(
!
$this
->
exists
)
{
$this
->
created_at
=
$this
->
updated_at
;
}
}
/**
* Delete a model from the database.
*
...
...
@@ -376,9 +394,13 @@ abstract class Eloquent {
*/
public
function
delete
(
$id
=
null
)
{
// If the delete method is being called on an existing model, we only want to delete
// that model. If it is being called from an Eloquent query model, it is probably
// the developer's intention to delete more than one model, so we will pass the
// delete statement to the query instance.
if
(
$this
->
exists
)
{
return
Query
::
table
(
static
::
table
(
get_class
(
$this
)))
->
delete
(
$this
->
id
);
return
Manager
::
connection
(
static
::
$connection
)
->
table
(
static
::
table
(
get_class
(
$this
)))
->
delete
(
$this
->
id
);
}
return
$this
->
query
->
delete
();
...
...
@@ -389,21 +411,24 @@ abstract class Eloquent {
*/
public
function
__get
(
$key
)
{
// The ignored attributes hold all of the loaded relationships for the model.
// Is the requested item a model relationship that has already been loaded?
// All of the loaded relationships are stored in the "ignore" array.
if
(
array_key_exists
(
$key
,
$this
->
ignore
))
{
return
$this
->
ignore
[
$key
];
}
//
If the attribute is a relationship method, return the related models
.
if
(
method_exists
(
$this
,
$key
))
// Is the requested item a model relationship? If it is, we will dynamically
//
load it and return the results of the relationship query
.
else
if
(
method_exists
(
$this
,
$key
))
{
$model
=
$this
->
$key
();
return
$this
->
ignore
[
$key
]
=
(
in_array
(
$this
->
relating
,
array
(
'has_one'
,
'belongs_to'
)))
?
$model
->
first
()
:
$model
->
get
();
}
return
(
array_key_exists
(
$key
,
$this
->
attributes
))
?
$this
->
attributes
[
$key
]
:
null
;
elseif
(
array_key_exists
(
$key
,
$this
->
attributes
))
{
return
$this
->
attributes
[
$key
];
}
}
/**
...
...
@@ -412,6 +437,7 @@ abstract class Eloquent {
public
function
__set
(
$key
,
$value
)
{
// If the key is a relationship, add it to the ignored attributes.
// Ignored attributes are not stored in the database.
if
(
method_exists
(
$this
,
$key
))
{
$this
->
ignore
[
$key
]
=
$value
;
...
...
@@ -444,20 +470,24 @@ abstract class Eloquent {
*/
public
function
__call
(
$method
,
$parameters
)
{
if
(
in_array
(
$method
,
array
(
'get'
,
'first'
,
'paginate'
)))
// To allow the "with", "get", "first", and "paginate" methods to be called both
// staticly and on an instance, we need to have private, underscored versions
// of the methods and handle them dynamically.
if
(
in_array
(
$method
,
array
(
'with'
,
'get'
,
'first'
,
'paginate'
)))
{
$method
=
'_'
.
$method
;
return
$this
->
$method
();
return
call_user_func_array
(
array
(
$this
,
'_'
.
$method
),
$parameters
);
}
// All of the aggregate functions can be passed directly to the query instance.
// For these functions, we can simply return the response of the query.
if
(
in_array
(
$method
,
array
(
'count'
,
'sum'
,
'min'
,
'max'
,
'avg'
)))
{
return
call_user_func_array
(
array
(
$this
->
query
,
$method
),
$parameters
);
}
// Pass the method to the query instance. This allows the chaining of methods
// from the query builder, providing a nice, convenient API.
// from the query builder, providing the same convenient query API as the
// query builder itself.
call_user_func_array
(
array
(
$this
->
query
,
$method
),
$parameters
);
return
$this
;
...
...
@@ -468,30 +498,8 @@ abstract class Eloquent {
*/
public
static
function
__callStatic
(
$method
,
$parameters
)
{
$model
=
static
::
make
(
get_called_class
());
if
(
$method
==
'all'
)
{
return
$model
->
_get
();
}
if
(
in_array
(
$method
,
array
(
'get'
,
'first'
,
'paginate'
)))
{
$method
=
'_'
.
$method
;
return
$model
->
$method
();
}
if
(
in_array
(
$method
,
array
(
'count'
,
'sum'
,
'min'
,
'max'
,
'avg'
)))
{
return
call_user_func_array
(
array
(
$model
->
query
,
$method
),
$parameters
);
}
// Pass the method to the query instance. This allows the chaining of methods
// from the query builder, providing a nice, convenient API.
call_user_func_array
(
array
(
$model
->
query
,
$method
),
$parameters
);
return
$model
;
// Just pass the method to a model instance and let the __call method take care of it.
return
call_user_func_array
(
array
(
static
::
query
(
get_called_class
()),
$method
),
$parameters
);
}
}
\ No newline at end of file
system/db/manager.php
0 → 100644
View file @
badc8a60
<?php
namespace
System\DB
;
use
System\Config
;
class
Manager
{
/**
* The established database connections.
*
* @var array
*/
public
static
$connections
=
array
();
/**
* Get a database connection. If no database name is specified, the default
* connection will be returned as defined in the db configuration file.
*
* Note: Database connections are managed as singletons.
*
* @param string $connection
* @return Connection
*/
public
static
function
connection
(
$connection
=
null
)
{
if
(
is_null
(
$connection
))
{
$connection
=
Config
::
get
(
'db.default'
);
}
if
(
!
array_key_exists
(
$connection
,
static
::
$connections
))
{
$config
=
Config
::
get
(
'db.connections'
);
if
(
!
array_key_exists
(
$connection
,
$config
))
{
throw
new
\Exception
(
"Database connection [
$connection
] is not defined."
);
}
static
::
$connections
[
$connection
]
=
new
Connection
(
$connection
,
(
object
)
$config
[
$connection
],
new
Connector
);
}
return
static
::
$connections
[
$connection
];
}
/**
* Begin a fluent query against a table.
*
* @param string $table
* @param string $connection
* @return Query
*/
public
static
function
table
(
$table
,
$connection
=
null
)
{
return
static
::
connection
(
$connection
)
->
table
(
$table
);
}
/**
* Magic Method for calling methods on the default database connection.
*/
public
static
function
__callStatic
(
$method
,
$parameters
)
{
return
call_user_func_array
(
array
(
static
::
connection
(),
$method
),
$parameters
);
}
}
\ No newline at end of file
system/db/query.php
View file @
badc8a60
<?php
namespace
System\DB
;
use
System\DB
;
use
System\Str
;
use
System\Config
;
class
Query
{
/**
* The database connection
name
.
* The database connection.
*
* @var string
*/
private
$connection
;
/**
* The database connection configuration.
*
* @var array
* @var Connection
*/
p
rivate
$config
;
p
ublic
$connection
;
/**
* The SELECT clause.
...
...
@@ -86,24 +78,25 @@ class Query {
/**
* Create a new query instance.
*
* @param string $table
* @param
string
$connection
* @param string
$table
* @param
Connection
$connection
* @return void
*/
public
function
__construct
(
$table
,
$connection
=
null
)
public
function
__construct
(
$table
,
$connection
)
{
$this
->
connection
=
(
is_null
(
$connection
))
?
Config
::
get
(
'db.default'
)
:
$connection
;
$this
->
from
=
'FROM '
.
$this
->
wrap
(
$this
->
table
=
$table
);
$this
->
connection
=
$connection
;
$this
->
table
=
$table
;
$this
->
from
=
'FROM '
.
$this
->
wrap
(
$table
);
}
/**
* Create a new query instance.
*
* @param string $table
* @param
string
$connection
* @param string
$table
* @param
Connection
$connection
* @return Query
*/
public
static
function
table
(
$table
,
$connection
=
null
)
public
static
function
table
(
$table
,
$connection
)
{
return
new
static
(
$table
,
$connection
);
}
...
...
@@ -129,22 +122,9 @@ class Query {
{
$this
->
select
=
(
$this
->
distinct
)
?
'SELECT DISTINCT '
:
'SELECT '
;
$wrapped
=
array
();
foreach
(
$columns
as
$column
)
{
// If the column name is being aliased, we will need to wrap the column
// name and its alias in keyword identifiers.
if
(
strpos
(
strtolower
(
$column
),
' as '
)
!==
false
)
{
$segments
=
explode
(
' '
,
$column
);
$wrapped
[]
=
$this
->
wrap
(
$segments
[
0
])
.
' AS '
.
$this
->
wrap
(
$segments
[
2
]);
}
else
{
$wrapped
[]
=
$this
->
wrap
(
$column
);
}
$wrapped
[]
=
$this
->
wrap
(
$column
);
}
$this
->
select
.=
implode
(
', '
,
$wrapped
);
...
...
@@ -194,6 +174,17 @@ class Query {
return
$this
->
join
(
$table
,
$column1
,
$operator
,
$column2
,
'LEFT'
);
}
/**
* Reset the where clause to its initial state.
*
* @return void
*/
public
function
reset_where
()
{
$this
->
where
=
'WHERE 1 = 1'
;
$this
->
bindings
=
array
();
}
/**
* Add a raw where condition to the query.
*
...
...
@@ -356,6 +347,47 @@ class Query {
return
$this
->
where_not_null
(
$column
,
'OR'
);
}
/**
* Add dynamic where conditions to the query.
*
* @param string $method
* @param array $parameters
* @return Query
*/
private
function
dynamic_where
(
$method
,
$parameters
)
{
// Strip the "where_" off of the method.
$finder
=
substr
(
$method
,
6
);
// Split the column names from the connectors.
$segments
=
preg_split
(
'/(_and_|_or_)/i'
,
$finder
,
-
1
,
PREG_SPLIT_DELIM_CAPTURE
);
// The connector variable will determine which connector will be used for the condition.
// We'll change it as we come across new connectors in the dynamic method string.
//
// The index variable helps us get the correct parameter value for the where condition.
// We increment it each time we add a condition.
$connector
=
'AND'
;
$index
=
0
;
foreach
(
$segments
as
$segment
)
{
if
(
$segment
!=
'_and_'
and
$segment
!=
'_or_'
)
{
$this
->
where
(
$segment
,
'='
,
$parameters
[
$index
],
$connector
);
$index
++
;
}
else
{
$connector
=
trim
(
strtoupper
(
$segment
),
'_'
);
}
}
return
$this
;
}
/**
* Add an ordering to the query.
*
...
...
@@ -394,49 +426,27 @@ class Query {
}
/**
*
Find a record by the primary key
.
*
Set the limit and offset values for a given page
.
*
* @param int
$id
* @param
array $columns
* @return
object
* @param int
$page
* @param
int $per_page
* @return
Query
*/
public
function
f
ind
(
$id
,
$columns
=
array
(
'*'
)
)
public
function
f
or_page
(
$page
,
$per_page
)
{
return
$this
->
where
(
'id'
,
'='
,
$id
)
->
first
(
$columns
);
return
$this
->
skip
((
$page
-
1
)
*
$per_page
)
->
take
(
$per_page
);
}
/**
*
Execute the query as a SELECT statement and return the first result
.
*
Find a record by the primary key
.
*
* @param int $id
* @param array $columns
* @return object
*/
public
function
first
(
$columns
=
array
(
'*'
))
{
return
(
count
(
$results
=
$this
->
take
(
1
)
->
get
(
$columns
))
>
0
)
?
$results
[
0
]
:
null
;
}
/**
* Execute the query as a SELECT statement.
*
* @param array $columns
* @return array
*/
public
function
get
(
$columns
=
array
(
'*'
))
public
function
find
(
$id
,
$columns
=
array
(
'*'
))
{
if
(
is_null
(
$this
->
select
))
{
$this
->
select
(
$columns
);
}
$results
=
DB
::
query
(
Query\Compiler
::
select
(
$this
),
$this
->
bindings
,
$this
->
connection
);
// Reset the SELECT clause so more queries can be performed using the same instance.
// This is helpful for performing counts and then getting actual results, such as
// when paginating results.
$this
->
select
=
null
;
return
$results
;
return
$this
->
where
(
'id'
,
'='
,
$id
)
->
first
(
$columns
);
}
/**
...
...
@@ -449,7 +459,6 @@ class Query {
private
function
aggregate
(
$aggregator
,
$column
)
{
$this
->
select
=
'SELECT '
.
$aggregator
.
'('
.
$this
->
wrap
(
$column
)
.
') AS '
.
$this
->
wrap
(
'aggregate'
);
return
$this
->
first
()
->
aggregate
;
}
...
...
@@ -462,9 +471,7 @@ class Query {
*/
public
function
paginate
(
$per_page
,
$columns
=
array
(
'*'
))
{
$select
=
$this
->
select
;
$total
=
$this
->
count
();
list
(
$select
,
$total
)
=
array
(
$this
->
select
,
$this
->
count
());
// Every query clears the SELECT clause, so we store the contents of the clause
// before executing the count query and then put the contents back in afterwards.
...
...
@@ -483,15 +490,53 @@ class Query {
}
/**
*
Set the LIMIT and OFFSET values for a given page
.
*
Execute the query as a SELECT statement and return the first result
.
*
* @param int $page
* @param int $per_page
* @return Query
* @param array $columns
* @return object
*/
public
function
f
or_page
(
$page
,
$per_page
)
public
function
f
irst
(
$columns
=
array
(
'*'
)
)
{
return
$this
->
skip
((
$page
-
1
)
*
$per_page
)
->
take
(
$per_page
);
return
(
count
(
$results
=
$this
->
take
(
1
)
->
get
(
$columns
))
>
0
)
?
$results
[
0
]
:
null
;
}
/**
* Execute the query as a SELECT statement.
*
* @param array $columns
* @return array
*/
public
function
get
(
$columns
=
array
(
'*'
))
{
if
(
is_null
(
$this
->
select
))
{
$this
->
select
(
$columns
);
}
$sql
=
$this
->
select
.
' '
.
$this
->
from
.
' '
.
$this
->
where
;
if
(
count
(
$this
->
orderings
)
>
0
)
{
$sql
.=
' ORDER BY '
.
implode
(
', '
,
$this
->
orderings
);
}
if
(
!
is_null
(
$this
->
limit
))
{
$sql
.=
' LIMIT '
.
$this
->
limit
;
}
if
(
!
is_null
(
$this
->
offset
))
{
$sql
.=
' OFFSET '
.
$this
->
offset
;
}
$results
=
$this
->
connection
->
query
(
$sql
,
$this
->
bindings
);
// Reset the SELECT clause so more queries can be performed using the same instance.
// This is helpful for getting aggregates and then getting actual results.
$this
->
select
=
null
;
return
$results
;
}
/**
...
...
@@ -502,7 +547,7 @@ class Query {
*/
public
function
insert
(
$values
)
{
return
DB
::
query
(
Query\Compiler
::
insert
(
$this
,
$values
),
array_values
(
$values
),
$this
->
connection
);
return
$this
->
connection
->
query
(
$this
->
compile_insert
(
$values
),
array_values
(
$values
)
);
}
/**
...
...
@@ -513,22 +558,40 @@ class Query {
*/
public
function
insert_get_id
(
$values
)
{
$sql
=
Query\Compiler
::
insert
(
$this
,
$values
);
$sql
=
$this
->
compile_insert
(
$values
);
// Use the RETURNING clause on Postgres instead of the PDO lastInsertID method.
// The PDO method is a little cumbersome using Postgres.
if
(
DB
::
driver
(
$this
->
connection
)
==
'pgsql'
)
if
(
$this
->
connection
->
driver
(
)
==
'pgsql'
)
{
$query
=
DB
::
connection
(
$this
->
connection
)
->
prepare
(
$sql
.
' RETURNING '
.
$this
->
wrap
(
'id'
));
$query
=
$this
->
connection
->
pdo
->
prepare
(
$sql
.
' RETURNING '
.
$this
->
wrap
(
'id'
));
$query
->
execute
(
array_values
(
$values
));
return
$query
->
fetch
(
\PDO
::
FETCH_CLASS
,
'stdClass'
)
->
id
;
}
DB
::
query
(
$sql
,
array_values
(
$values
),
$this
->
connection
);
$this
->
connection
->
query
(
$sql
,
array_values
(
$values
)
);
return
DB
::
connection
(
$this
->
connection
)
->
lastInsertId
();
return
$this
->
connection
->
pdo
->
lastInsertId
();
}
/**
* Compile an SQL INSERT statement.
*
* @param array $values
* @return string
*/
private
function
compile_insert
(
$values
)
{
$sql
=
'INSERT INTO '
.
$this
->
wrap
(
$this
->
table
);
foreach
(
array_keys
(
$values
)
as
$column
)
{
$columns
[]
=
$this
->
wrap
(
$column
);
}
return
$sql
.=
' ('
.
implode
(
', '
,
$columns
)
.
') VALUES ('
.
$this
->
parameterize
(
$values
)
.
')'
;
}
/**
...
...
@@ -539,7 +602,16 @@ class Query {
*/
public
function
update
(
$values
)
{
return
DB
::
query
(
Query\Compiler
::
update
(
$this
,
$values
),
array_merge
(
array_values
(
$values
),
$this
->
bindings
),
$this
->
connection
);
$sql
=
'UPDATE '
.
$this
->
wrap
(
$this
->
table
)
.
' SET '
;
foreach
(
array_keys
(
$values
)
as
$column
)
{
$sets
[]
=
$this
->
wrap
(
$column
)
.
' = ?'
;
}
$sql
.=
implode
(
', '
,
$sets
)
.
' '
.
$this
->
where
;
return
$this
->
connection
->
query
(
$sql
,
array_merge
(
array_values
(
$values
),
$this
->
bindings
));
}
/**
...
...
@@ -555,32 +627,38 @@ class Query {
$this
->
where
(
'id'
,
'='
,
$id
);
}
return
DB
::
query
(
Query\Compiler
::
delete
(
$this
),
$this
->
bindings
,
$this
->
connection
);
return
$this
->
connection
->
query
(
'DELETE FROM '
.
$this
->
wrap
(
$this
->
table
)
.
' '
.
$this
->
where
,
$this
->
bindings
);
}
/**
* Wrap a value in keyword identifiers.
*
* @param string $value
* @param string
$value
* @return string
*/
p
ublic
function
wrap
(
$value
)
p
rivate
function
wrap
(
$value
)
{
if
(
is_null
(
$this
->
config
)
)
if
(
strpos
(
strtolower
(
$value
),
' as '
)
!==
false
)
{
$connections
=
Config
::
get
(
'db.connections'
);
$this
->
config
=
$connections
[
$this
->
connection
];
return
$this
->
wrap_alias
(
$value
,
$connection
);
}
if
(
array_key_exists
(
'wrap'
,
$this
->
config
)
and
$this
->
config
[
'wrap'
]
===
false
)
{
return
$value
;
}
$wrap
=
$this
->
connection
->
wrapper
();
$wrap
=
(
DB
::
driver
(
$this
->
connection
)
==
'mysql'
)
?
'`'
:
'"'
;
return
implode
(
'.'
,
array_map
(
function
(
$segment
)
use
(
$wrap
)
{
return
(
$segment
!=
'*'
)
?
$wrap
.
$segment
.
$wrap
:
$segment
;
},
explode
(
'.'
,
$value
)));
}
/**
* Wrap an alias in keyword identifiers.
*
* @param string $value
* @return string
*/
private
function
wrap_alias
(
$value
)
{
$segments
=
explode
(
' '
,
$value
);
return
implode
(
'.'
,
array_map
(
function
(
$segment
)
use
(
$wrap
)
{
return
(
$segment
!=
'*'
)
?
$wrap
.
$segment
.
$wrap
:
$segment
;},
explode
(
'.'
,
$value
))
);
return
$this
->
wrap
(
$segments
[
0
])
.
' AS '
.
$this
->
wrap
(
$segments
[
2
]
);
}
/**
...
...
@@ -589,7 +667,7 @@ class Query {
* @param array $values
* @return string
*/
p
ublic
function
parameterize
(
$values
)
p
rivate
function
parameterize
(
$values
)
{
return
implode
(
', '
,
array_fill
(
0
,
count
(
$values
),
'?'
));
}
...
...
@@ -601,7 +679,7 @@ class Query {
{
if
(
strpos
(
$method
,
'where_'
)
===
0
)
{
return
Query\Dynamic
::
build
(
$method
,
$parameters
,
$this
);
return
$this
->
dynamic_where
(
$method
,
$parameters
,
$this
);
}
if
(
in_array
(
$method
,
array
(
'count'
,
'min'
,
'max'
,
'avg'
,
'sum'
)))
...
...
system/db/query/compiler.php
deleted
100644 → 0
View file @
829088f3
<?php
namespace
System\DB\Query
;
class
Compiler
{
/**
* Build a SQL SELECT statement.
*
* @param Query $query
* @return string
*/
public
static
function
select
(
$query
)
{
$sql
=
$query
->
select
.
' '
.
$query
->
from
.
' '
.
$query
->
where
;
if
(
count
(
$query
->
orderings
)
>
0
)
{
$sql
.=
' ORDER BY '
.
implode
(
', '
,
$query
->
orderings
);
}
if
(
!
is_null
(
$query
->
limit
))
{
$sql
.=
' LIMIT '
.
$query
->
limit
;
}
if
(
!
is_null
(
$query
->
offset
))
{
$sql
.=
' OFFSET '
.
$query
->
offset
;
}
return
$sql
;
}
/**
* Build a SQL INSERT statement.
*
* @param Query $query
* @param array $values
* @return string
*/
public
static
function
insert
(
$query
,
$values
)
{
$sql
=
'INSERT INTO '
.
$query
->
wrap
(
$query
->
table
);
$columns
=
array
();
foreach
(
array_keys
(
$values
)
as
$column
)
{
$columns
[]
=
$query
->
wrap
(
$column
);
}
return
$sql
.=
' ('
.
implode
(
', '
,
$columns
)
.
') VALUES ('
.
$query
->
parameterize
(
$values
)
.
')'
;
}
/**
* Build a SQL UPDATE statement.
*
* @param Query $query
* @param array $values
* @return string
*/
public
static
function
update
(
$query
,
$values
)
{
$sql
=
'UPDATE '
.
$query
->
wrap
(
$query
->
table
)
.
' SET '
;
$sets
=
array
();
foreach
(
array_keys
(
$values
)
as
$column
)
{
$sets
[]
=
$query
->
wrap
(
$column
)
.
' = ?'
;
}
return
$sql
.=
implode
(
', '
,
$sets
)
.
' '
.
$query
->
where
;
}
/**
* Build a SQL DELETE statement.
*
* @param Query $query
* @return string
*/
public
static
function
delete
(
$query
)
{
return
'DELETE FROM '
.
$query
->
wrap
(
$query
->
table
)
.
' '
.
$query
->
where
;
}
}
\ No newline at end of file
system/db/query/dynamic.php
deleted
100644 → 0
View file @
829088f3
<?php
namespace
System\DB\Query
;
use
System\Str
;
class
Dynamic
{
/**
* Add conditions to a query from a dynamic method call.
*
* @param string $method
* @param array $parameters
* @param Query $query
* @return Query
*/
public
static
function
build
(
$method
,
$parameters
,
$query
)
{
// Strip the "where_" off of the method.
$finder
=
substr
(
$method
,
6
);
// Split the column names from the connectors.
$segments
=
preg_split
(
'/(_and_|_or_)/i'
,
$finder
,
-
1
,
PREG_SPLIT_DELIM_CAPTURE
);
// The connector variable will determine which connector will be used for the condition.
// We'll change it as we come across new connectors in the dynamic method string.
//
// The index variable helps us get the correct parameter value for the where condition.
// We increment it each time we add a condition.
$connector
=
'AND'
;
$index
=
0
;
foreach
(
$segments
as
$segment
)
{
if
(
$segment
!=
'_and_'
and
$segment
!=
'_or_'
)
{
if
(
!
array_key_exists
(
$index
,
$parameters
))
{
throw
new
\Exception
(
"Wrong number of parameters for dynamic finder [
$method
]."
);
}
$query
->
where
(
$segment
,
'='
,
$parameters
[
$index
],
$connector
);
$index
++
;
}
else
{
$connector
=
trim
(
strtoupper
(
$segment
),
'_'
);
}
}
return
$query
;
}
}
\ No newline at end of file
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