Permalink
Browse files

Expanding the User class a bit.

Got rid of the previous idea about databases and replacing it with a new one.
Signed-off-by: Michael Miller <mikemill@gmail.com>
  • Loading branch information...
1 parent 5cc5dd9 commit 4c72cf870f1efc8ba7b424cc60fec51281984c69 @mikemill committed May 24, 2012
@@ -5,7 +5,8 @@
{
protected $conns = array();
protected $settings = array();
- protected $queries = array();
+ protected $dbtypename = '';
+ protected $dbstorages = array();
public function __construct($settings)
{
@@ -14,44 +15,23 @@ public function __construct($settings)
$this->connect();
}
- abstract protected function query($query, $replacements = array(), $conn = 'host');
- abstract public function insert($table, $columns, $data, $querytype = 'insert', $conn = 'insert');
+ abstract public function query($query, $replacements = array(), $conn = 'host');
+ abstract public function insert($table, $columns, $data, $querytype = 'insert', $conn = 'write');
+ abstract public function update($table, $columns, $data, $conn = 'write');
abstract protected function connect($set = 'all', $select_db = true, $reconnect = false);
- public function select($query, $replacements = array(), $conn = 'select', $rawsql = false)
+ public function select($query, $replacements = array(), $conn = 'select')
{
- if (!$rawsql)
- {
- list ($namespace, $queryns, $query) = explode(':', $query);
-
- if (!isset($this->queries[$namespace], $this->queries[$namespace][$queryns]))
- $this->load_queries($namespace, $queryns);
-
- $query = $this->queries[$namespace][$queryns][$query];
- }
-
return $this->query($query, $replacements, $conn);
}
- protected function load_queries($namespace, $queryns)
+ public function loadStorage($class, $namespace = '\\JawHare\\Storage\\')
{
- $classdir = str_replace(array(__NAMESPACE__, '\\'), array('', '/'), get_called_class());
- if ($namespace == 'JawHare')
- $queryfile = $this->settings['queries_dir'] . $classdir . '/' . $queryns . '.sql';
- else
- $queryfile = $this->settings['module_queries_dir'] . $classdir . '/' . $queryns . '.sql';
-
- $contents = file_get_contents($queryfile);
-
- preg_match_all('~/\* ?\[([0-9a-zA-Z_\-]+)\] begin ?\*/(.*?)/\* ?\[\1\] end ?\*/~s', $contents, $matches, PREG_SET_ORDER);
-
- $queries = array();
+ $class = $namespace . $class . 'Storage' . $this->dbtypename;
- foreach ($matches AS $match)
- {
- $queries[$match[1]] = trim($match[2]);
- }
+ if (!isset($this->dbstorages[$class]))
+ $this->dbstorages[$class] = new $class($this);
- $this->queries[$namespace][$queryns] = $queries;
+ return $this->dbstorages[$class];
}
}
@@ -3,14 +3,16 @@
namespace JawHare\Database;
class DatabaseMySQL extends Database
{
+ protected $dbtypename = 'MySQL';
+
public function __construct($settings)
{
$this->settings = $settings;
$this->connect();
}
- protected function query($query, $replacements = array(), $conn = 'host')
+ public function query($query, $replacements = array(), $conn = 'host')
{
$sql = preg_replace_callback('~\{([a-zA-Z_\-0-9]+):([a-zA-Z_\-0-9]+)\}~', function($matches) use ($replacements)
{
@@ -23,8 +25,15 @@ protected function query($query, $replacements = array(), $conn = 'host')
return '"' . mysql_real_escape_string((string) $value) . '"';
case 'sqlid':
return '`' . (string) $value . '`';
+ case 'array_identifiers':
+ foreach ($value AS &$val)
+ $val = '`' . (string) $val . '`';
+ unset($val);
+ return implode(', ', $value);
@norv

norv May 24, 2012

Contributor

Nice. One of the replacements that make sense, useful often enough, and obvious enough I'd say.

case 'int':
return (string) (int) $value;
+ case 'bool':
+ return ((bool) $value) ? '1' : '0';
}
return $matches[0];
@@ -38,14 +47,17 @@ protected function query($query, $replacements = array(), $conn = 'host')
return new DatabaseMySQLResult($result, $this->conns[$conn], $sql);
}
- public function insert($table, $columns, $data, $querytype = 'insert', $conn = 'insert')
+ public function insert($table, $columns, $data, $querytype = 'insert', $conn = 'write')
{
$replacements = array(
'table' => $table,
);
$columnnames = array_keys($columns);
+ $columnnamesrev = array_flip($columnnames);
$columntypes = array_values($columns);
+ echo '<pre>'; print_r($columns); print_r($columnnames); print_r($columnnamesrev); print_r($data); die('</pre>');
+
$colcount = count($columns);
$sql = strtoupper($querytype) . ' INTO {sqlid:table} (';
@@ -77,13 +89,19 @@ public function insert($table, $columns, $data, $querytype = 'insert', $conn = '
$sql .= ',';
}
+ die("<hr><pre>$sql<hr>" . print_r($replacements, true) . "</pre>");
+
return $this->query($sql, $replacements, $conn);
}
+ public function update($table, $columns, $data, $conn = 'write')
+ {
+ }
+
protected function connect($set = 'all', $select_db = true, $reconnect = false)
{
if ($set == 'all')
- $set = array('host', 'select', 'insert');
+ $set = array('host', 'select', 'write');
elseif (!is_array($set))
$set = array($set);
@@ -0,0 +1,18 @@
+<?php
+
+namespace JawHare\Storage;
+class DatabaseStorage
+{
+ protected $db = null;
+ protected $columns = array();
+
+ public function __construct($db)
+ {
+ $this->db = $db;
+ }
+
+ protected function colSQLID($col)
@norv

norv May 24, 2012

Contributor

This is myMethodIDS() style.

+ {
+ return '{' . $this->columns[$col] . ':' . $col . '}';
@norv

norv May 24, 2012

Contributor

Oh interesting.
ETA: I'm not sure how it plays out with relations.

+ }
+}
@@ -0,0 +1,19 @@
+<?php
+
+namespace JawHare\Storage;
+
+abstract class UserStorage extends DatabaseStorage
@norv

norv May 24, 2012

Contributor

This is much nicer and understandable - actually designing the classes with an abstract parent, and get hold of $db since you're using it all the time anyway.

Small note: it means you always do load... 4 files, in order to load a XxxStorage class. Probably negligible though. (a common default would be 1-2 less). Everything is quite nicely laid out though.

+{
+ protected $columns = array(
@norv

norv May 24, 2012

Contributor

Suitable and easy for very obvious model classes, and their respective db operations. Lets see when we get to relations. :D

@norv

norv May 24, 2012

Contributor

Every model class or almost every model class, of the application domain, will have basic CRUD anyway. And I find this all very readable and easy to work with, for both client code, as well as the framework code itself.

+ 'id_user' => 'int',
+ 'username' => 'string',
+ 'fullname' => 'string',
+ 'passwd' => 'string',
+ 'email' => 'string',
+ 'admin' => 'bool',
+ );
+ abstract public function load_user($id);
+ abstract public function load_user_by_username($name);
+ abstract public function create_user($data);
+ abstract public function update_user($data);
+}
@@ -0,0 +1,69 @@
+<?php
+
+namespace JawHare\Storage;
+
+class UserStorageMySQL extends UserStorage
+{
+ public function load_user($id)
+ {
+ return $this->db->select('
+ SELECT {array_identifiers:cols}
+ FROM users
+ WHERE id_user = ' . $this->colSQLID('username'),
+ array(
+ 'cols' => array_keys($this->columns),
+ 'id' => $id,
+ )
+ );
+ }
+
+ public function load_user_by_username($name)
+ {
+ return $this->db->select('
+ SELECT {array_identifiers:cols}
+ FROM users
+ WHERE username = ' . $this->colSQLID('username'),
+ array(
+ 'cols' => array_keys($this->columns),
+ 'username' => $name,
+ )
+ );
+ }
+
+ public function create_user($data)
+ {
+ $cols = $this->columns;
+ unset($cols['id_user']);
+
+ $colnames = array_keys($cols);
+ $coltypes = array();
+ foreach ($cols AS $name => $type)
+ $coltypes[] = $this->colSQLID($name);
+
+ $data['cols'] = $colnames;
+
+ return $this->db->query('
+ INSERT INTO users ({array_identifiers:cols})
+ VALUES (' . implode(', ', $coltypes) . ')'
+ , $data, 'write');
+ }
+
+ public function update_user($data)
+ {
+ $columns = array();
+
+ foreach ($data AS $col => $val)
+ {
+ if ($col == 'id_user')
+ continue;
+ $columns[] = $col . ' = ' . $this->colSQLID($col);
+ }
+
+ return $this->db->query('
+ UPDATE users SET
+ ' . implode(', ', $columns) . '
+ WHERE id_user = ' . $this->colSQLID('id_user'),
+ $data,
+ 'write');
+ }
+}
View
@@ -6,15 +6,30 @@ class User
protected $id;
protected $hashed_pw = '';
protected $data = array();
+ protected $storage = null;
+ protected $dirty = false;
public function __construct($id = null)
{
$this->id = $id;
+ $this->storage = Database()->loadStorage('User');
+
if ($this->id !== null)
$this->load();
}
+ protected function load_from_array($array)
+ {
+ foreach ($array AS $data => $value)
+ {
+ $this->data[$data] = $value;
@norv

norv May 24, 2012

Contributor

Not sure how maintainable this will be. It's a bit surprising in a way, to have the fields defining a user (more) explicit in the database layer class and not have them in the model class. Then again, I'm not exactly suggesting duplication.
Looking at everything here... it makes perfect sense. (perhaps slightly less 'admin'?).

From the client code perspective, I care to know how to retrieve and change the User properties, and I can do that very well. From a framework module/developer/contributor perspective... it depends. Explicit variables/properties in the class, as opposed to an array (which "obscures" them) may be preferable, and it is the style of some projects.
Personally, depends on the case. A dozen-something of explicit properties to fill your first page of the class declaration isn't exactly ideal either. :D (and I'm kinda tired of it). It just depends.
So far, this is very readable IMHO and invites to be used. :)

+ }
+
+ $this->hashed_pw = $this->data['passwd'];
+ $this->id = $this->data['id_user'];
+ }
+
public function hashpw($pw)
{
// This function uses the Blowfish algorithm with a random salt.
@@ -51,16 +66,94 @@ public function load($id = null)
if ($id === null)
$id = $this->id;
- $db = Database();
+ if (is_numeric($id))
+ $ret = $this->storage->load_user($id);
+ elseif (is_string($id))
+ $ret = $this->storage->load_user_by_username($id);
+ else
+ return $this;
- $ret = $db->select('JawHare:User:load_user', array('id' => $id));
+ if ($ret->numrows() == 0)
+ throw new \Exception('Unable to load member');
+ $this->load_from_array($ret->assoc());
- foreach ($ret->assoc() AS $data => $value)
+ return $this;
+ }
+
+ public function save()
+ {
+ if (empty($this->id))
{
- $this->data[$data] = $value;
+ $ret = $this->storage->create_user($this->data);
+ $this->id($ret->insert_id());
}
+ else
+ $this->storage->update_user($this->data);
+ }
- $this->hashed_pw = $this->data['passwd'];
- $this->id = $this->data['id_user'];
+ public function id ($val = null)
+ {
+ if ($val === null)
+ return $this->id;
+ else
+ {
+ $this->id = $this->data['id_user'] = $val;
+ return $this;
+ }
+ }
+
+ public function username ($val = null)
+ {
+ if ($val === null)
+ return $this->data['username'];
+ else
+ {
+ $this->data['username'] = $val;
+ return $this;
+ }
+ }
+
+ public function fullname ($val = null)
+ {
+ if ($val === null)
+ return $this->data['fullname'];
+ else
+ {
+ $this->data['fullname'] = $val;
+ return $this;
+ }
+ }
+
+ public function email ($val = null)
+ {
+ if ($val === null)
+ return $this->data['email'];
+ else
+ {
+ $this->data['email'] = $val;
+ return $this;
+ }
+ }
+
+ public function admin ($val = null)
+ {
+ if ($val === null)
+ return $this->data['admin'];
+ else
+ {
+ $this->data['admin'] = (bool) $val;
+ return $this;
+ }
+ }
+
+ public function passwd ($val = null)
+ {
+ if ($val === null)
+ return $this->data['passwd'];
+ else
+ {
+ $this->hashed_pw = $this->data['passwd'] = $this->hashpw($val);
+ return $this;
+ }
}
}

0 comments on commit 4c72cf8

Please sign in to comment.