I'm developing an object-oriented PHP website right now and am trying to determine the best way to abstract database functionality from the rest of the system. Right now, I've got a DB class that manages all the connections and queries that the system uses (it's pretty much an interface to MDB2). However, when using this system, I've realized that I've got a lot of SQL query strings showing up everywhere in my code. For instance, in my User class, I've got something like this:
function checkLogin($email,$password,$remember=false){
$password = $this->__encrypt($password);
$query = "SELECT uid FROM Users WHERE email=? AND pw=?";
$result = $this->db->q($query,array($email,$password));
if(sizeof($result) == 1){
$row = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
$uid = $row['uid'];
}else{
return false;
}
/* Rest of the login script */
}
What I would like to do is find out the best technique for reducing the amount of inline SQL. I understand that one way to do this would be to write functions within User for each of the queries that User makes use of (something like the following), but that could lead to quite a few functions.
function checkLogin($email,$password,$remember=false){
$password = $this->__encrypt($password);
$uid = $this->do_verify_login_query($email,$password);
/* Rest of the login script */
}
function do_verify_login_query($email,$encpw){
$query = "SELECT uid FROM Users WHERE email=? AND pw=?";
$result = $this->$db->q($query,array($email,$encpw));
if(sizeof($result) == 1){
$row = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
return $row['uid'];
}else{
return false;
}
}
So...my question. What is the best technique for managing the large amount of queries that a typical database application would use? Would the way I described be the proper way of handling this situation? Or what about registering a list of queries within the DB class and associating with each a unique ID (such as USER_CHECKLOGIN) that is passed into the DB's query function? This method could also help with security, as it would limit the queries that could be run to only those that are registered in this list, but it's one more thing to remember when writing all the class functions. Thoughts?
Having the SQL pulled out into separate functions is a decent start. Some other things you can do:
Create separate classes for database access code. This will help make sure you don't have SQL functions scattered around in all of your PHP files.
Load the SQL from external files. This completely separates your SQL code and your PHP code, making both more maintainable.
Use stored procedures when you can. This removes the SQL from your PHP code altogether, and helps improve your database security by reducing the risk that external SQL will get executed.
You might want to look into implementing the ActiveRecord Pattern. Using a design pattern such as this provides some consistency in how you work with data from your tables. There can be some downsides to these sorts of approaches, mainly performance for certain types of queries but it can be worked around.
Another option can be the use of an ORM, for PHP the most powerful are:
Propel
Doctrine
Both allow you to access your database using a set of objects, providing a simple API for storing and querying data, both have their own query language, that is converted internally to the targeted DBMS native SQL, this will ease migrating applications from one RDBMS to another with simple configuration changes. I also like the fact that you can encapsulate datamodel logic to add validation for example, only by extending your model classes.
Since you say you're doing this as OO PHP, then why do you have SQL scattered through all the methods in the first place? More common models would be:
Use an ORM and let that handle the database.
Give your classes one or more 'load' methods which use a single query to pull all of an object's data into memory and a 'save' method which uses a single query to update everything in the database. All the other methods only need to do in-memory manipulation and the database interactions are confined to the load/save methods.
The first option will generally be more robust, but the second may run faster and will probably feel more familiar compared to the way you're used to doing things, if either of those are concerns.
For your login example, the way I would do it, then, would be to simply load the user by email address, call $user->check_password($entered_password), and throw an exception/return false/whatever if check_password fails. Neither check_password nor any of the login handling code need to concern themselves with the database, or even with knowing that a database is where the user gets loaded from.
Another option is to think of the queries as data and store them in the database. For instance, you can create one table that stores the query with a name and another table that stores the parameters for that query. Then create a function in PHP that takes the name of the query and an array of params and executes the query, returning any results. You could also attach other metadata to the queries to restrict access to certain users, apply post-functions to the results, etc.
Related
Please note I'm not looking for 'use a framework' answers. I'm trying to structurally improve the way I code websites and approach databases from PHP.
I'm building a web service from scratch, without any frameworks. I'm using a LAMP stack and am trying to learn a bit of PHP's OO functionality while I'm at it. I've previously only used OO to make mobile apps.
I've been at it for months now (as planned, no worries). Along the way I've bumped into a couple of structural problems, making me wonder what the best way would be to make the code object oriented.
Pretty much all of the problems involve the database in some way. Say we have a class DB and a class User. In most cases I only need to fetch a single user's information from the database. I thought a good way to handle it was to have a global $_db variable and have the User object query the database like so (oversimplified):
class User {
function __construct($id) {
global $_db;
$q = $_db->query("SELECT name, mail FROM user WHERE id = ?", $id);
$this->loadProperties($q);
}
}
Now say we have a page that shows a list of users. I still want to make User objects for each of them, but I don't want to query the database for each separate user.
So, I extend the User class to take an object as an argument:
class User {
function __construct($id) {
if(is_object($id))
$q = $id;
else {
global $_db;
$q = $_db->query("SELECT name, mail FROM user WHERE id = ?", $id);
}
$this->loadProperties($q);
}
}
Now I can create a list of, for example, the 100 most recently created and active accounts:
$user_list = [];
$q = $_db->query("SELECT name, mail FROM user WHERE banned = 0 ORDER BY date_created DESC LIMIT 100");
while($a = $_db->fetch($q))
$user_list[] = new User($a);
This all works great, except for one big downside: the database queries for table user are no longer in one place, which is kind of making spaghetti code. This is where I'm starting to wonder whether this can be done more efficiently.
So maybe I need to extend my DB object instead of my User object, for example:
class DB {
public function getUsers($where) {
$q = $this->query("SELECT name, mail FROM user WHERE ".$where);
$users = [];
while($a = $this->fetch($q))
$users[] = new User($a);
}
}
Now I would create the user list as follows:
$user_list = $_db->getUsers("banned = 0 ORDER BY date_created DESC LIMIT 100");
But now I'm calling the getUsers() method in various places using various SQL queries, solving nothing. I also don't want to load the same properties each time, so my getUsers() method will have to take entire SQL queries as an argument. Anyway, you get the point.
Speaking of loading different properties, there's another thing that has been bugging me writing OO in PHP. Let's assume our PHP object has at least every property our database row has. Say I have a method User::getName():
class User {
public function getName() {
return $this->name;
}
}
This function will assume the appropriate field has been loaded from the database. However it would be inefficient to preload all of the user's properties each time I make an object. Sometimes I'll only need the user's name. On the other hand it would also be inefficient to go into the database at this point to load this one property.
I have to make sure that for each method I use, the appropriate properties have already been loaded. This makes complete sense from a performance perspective, but from an OO perspective, it means you have to know beforehand which methods you're gonna use which makes it a lot less dynamic and, again, allows for spaghetti code.
The last thing I bumped into (for now at least), is how to separate actual new users from new User. I figured I'd use a separate class called Registration (again, oversimplified):
class Registration {
function createUser() {
$form = $this->getSubmittedForm();
global $_db;
$_db->query("INSERT INTO user (name, mail) VALUES (?, ?)", $form->name, $form->mail);
if($_db->hasError)
return FALSE;
return $_db->insertedID;
}
}
But this means I have to create two separate classes for each database table and again I have different classes accessing the same table. Not to mention there's a third class handling login sessions that's also accessing the user table.
In summary, I feel like all of the above can be done way more efficiently. Most importantly I want pretty code. I feel like I'm missing a way to approach the database from an OO perspective. But how can I do so without losing the dynamics and power of SQL queries?
I'm looking forward to reading your experiences and ideas in this field.
Update
Seems most of you condemn my use of global $_db. Though you've convinced me this isn't the best approach, for the scope of this question it's irrelevant whether I'm supplying the database through an argument, a global or a singleton. It's still a separate class DB that handles any interaction with the database.
It's a common thing to have a separate class to handle SQL queries and to keep the fetched data. In fact, it is the real application of the Single Responsibility Principle.
What I usually do is keep a class with all the information concerning the data, in your case the User class, with all the user information as fields.
Then comes the business layer, for instance UserDataManager (though the use of "Manager" as a suffix is not recommended and you'd better find a more suitable name in each scenario) which takes the pdo object in its constructor to avoid use of global variables and has all the SQL methods. You'd thus have methods registerNewUser, findUserById, unsuscribeUser and so on (the use of "User" in the method can be implied by the class name and be omitted).
Hope it helps.
I've liked to use the data mapper pattern (or at least I think that's how I'm doing it). I've done this for some sites built on Silex, though it's applicable to going without a framework since Silex is very lightweight and doesn't impose much on how you architect your code. In fact, I recommend you check out Symfony2/Silex just to get some ideas for ways to design your code.
Anyway, I've used classes like UserMapper. Since I was using the Doctrine DBAL library, I used Dependency injection to give each mapper a $db. But the DBAL is pretty much a wrapper on the PDO class as far as I understand, so you could inject that instead.
Now you have a UserMapper who is responsible for the CRUD operations. So I solve your first problem with methods like LoadUser($id) and LoadAllUsers(). Then I would set all the properties on the new User based on the data from the database. You can similarly have CreateUser(User $user). Note that in "create", I'm really passing a User object and mapping it to the database. You could call it PersistUser(User $user) to make this distinction more clear. Now all of the SQL queries are in one place and your User class is just a collection of data. The User doesn't need to come from the database, you could create test users or whatever else without any modification. All of the persistence of `User is encapsulated in another class.
I'm not sure that it's always bad to load all of the properties of a user, but if you want to fix that, it's not hard to make LoadUsername($id) or other methods. Or you could do LoadUser($id, array $properties) with a set of properties taht you want to load. If your naming is consistent, then it's easy to have set the properties like:
// in a foreach, $data is the associative array returned by your SQL
$setter = 'set'.$property;
$user->$setter($data[$property]);
Or (and?) you could solve this with Proxy objects. I haven't done this, but the idea is to return a bunch of UserProxy objects, which extend User. But they have the Mapper injected and they override the getters to call into the Mapper to select more. Perhaps when you access one property on a proxy, it will select everything via the mapper (a method called populateUser($id)?) and then subsequent getters can just access the properties in memory. This might make some sense if you, for example, select all users then need to access data on a subset. But I think in general it may be easier to select everything.
public function getX()
{
if (!isset($this->x)) {
$this->mapper->populateUser($this);
}
return $this->x;
}
For new users, I say just do $user = new User... and set everything up, then call into $mapper->persist($user). You can wrap that up in another class, like UserFactory->Create($data) and it can return the (persisted) User. Or that class can be called Registration if you'd like.
Did I mention you should use Dependency Injection to handle all of these services (like the Mappers and others like Factories maybe)? Maybe just grab the DIC from Silex, called Pimple. Or implement a lightweight one yourself (it's not hard).
I hope this helps. It's a high-level overview of some things I've picked up from writing a lot of PHP and using Syfmony2/Silex. Good luck and glad to see PHP programmers like yourself actually trying to "do things right"! Please comment if I can elaborate anywhere. Hope this answer helps you out.
You should first begin by writing a class as a wrapper to your Database object, which would be more clean that a global variable (read about the Singleton Pattern if you don't know it, and there is a lot of examples of Singleton Database Wrapper on the web). You'll then have a better view of the architecture you should implement.
Best is to separate datas from transactions with the database, meaning that you can for example have two classes for your User ; one that will only send queries and fetch responses, and the other that will manage datas thanks to object's attributes and methods. Sometimes, there can be also some action that doesn't require to interact with the database, and that would be implemented in these classes too.
Last but not least, it can be a good idea to look a MVC frameworks and how they work (even if you don't want to use it) ; that would give you a good idea of how can be structured a web application, and how to implement these pattern for you in some way.
I have four forms on my website sending user input to four different tables in my MySQL database. Is it good practice to put all the queries for those four forms in a single php file?
Currently, I have a different php file for each form. Eg. form-abc.html has a abc.php to communicate with the database, form-def.html has a def.php and so on.
Since the mysqli connection in all these forms is going to be the same, I was wondering if it is possible to call the relevant function/ query from a php file containing all the queries/functions? And if it is possible, how?
On balance it is not good practice to put the code in a single file, particularly if the four queries are not obviously related in terms of their purpose, but it would also be poor design to have each file containing the code to connect to the database, and also to be hand crafting the query strings.
You should seek to factor out common code to a utility library, with your starting point being to have a single library function that returns a connection to the database. Changing connection details, the name of the database etc. then requires a single change rather than four, and so reducing the chances of a mistake. Your four files should also not need to know how to connect to the database, and removing the code achieves that.
Rather than hand crafting the queries, where there is a chance of failing to properly escape data and where you will end up writing more code than is necessary as well as locking yourself to a particular database type, you should aim to have library routines to help with this. PHP has some libraries already, or you could develop your own that are potentially better suited for the job.
Very broadly, you may end up with code such as this:
<?php
include_once "db_utils.php";
$db = DB::get_instance();
if (isset($_POST['form_submitted'])) {
// Validation etc.
$customers = $db->table('customers');
$customers->insert(array('name' => $_POST['name'], 'email' => $_POST['email']));
// ...
}
The include would include your database utilities, providing in this case a class called DB, and a table class. get_instance() would be a static method to provide database instance that encapsulates a connection. We'll assume that it knows how to obtain connection information, so not needing that to be provided to the method. The table() method would provide an instance of a database table that would know how to perform operations on a database table. The insert() method on a table instance would take an array of key/value pair data, escape the value of each item and do the insert.
Keeping the queries with the files that need them should in this case ease maintenance.
An alternative approach to your entire site would be to use a framework. In such a case, the business logic, database management and rendering code would be separated, which for non-trivial systems is generally a desirable aspiration. If one cobbles a system together with that in mind in an ad-hoc way, this could quickly lead to an unmaintainable system, with developers struggling to find the relevant code that plays a part in a request, however a framework would impose a structure through naming and layout conventions that would largely alleviate that.
For where you are now though, just aim to identify duplicate code, factor that out, look for some database libraries so that you are not hand crafting queries (if you are right now), and keep your system simple.
I'm working with the TinyMVC framework, and it allows one to "build MySQL queries programmatically, much like active record." The example is embedded here (relevant TinyMVC documentation):
class Members_Model extends TinyMVC_Model
{
function get_members()
{
$this->db->select('foo,bar,baz'); // set selected columns
$this->db->from('mytable'); // set from what table(s)
$this->db->where('foo','test'); // where foo='test'
$this->db->orwhere('foo=? and bar=?',array('test','test2')) // where foo='test' and bar='test2'
$this->db->join('jointable','mytable','jointable.foo=mytable.foo'); // join tables on (optional) condition
$this->db->in('mycolumn',$elements,$islist,$prefix) // IN clause: column, elements (comma-separated or array), $list=boolean is list or array, $prefix: AND|OR
$this->db->orderby('ordercolumn'); // order by column(s)
$this->db->groupby('groupbycolumn'); // group by column(s)
$this->db->limit($limit,$offset); // query limit, optional offset
$this->db->query();
while($row = $this->db->next()) {
$rows[] = $row;
}
return $rows;
}
}
How is this different or better than the writing the SQL query outright:
SELECT foo, bar, baz
FROM mytable
WHERE...
The benefit is that you can have interdependent functions in your controller that can build on your query without having to worry about the order of your SQL. You can have conditional logic to use certain active record manipulations on one query and then simply run it when it's fully populated.
CodeIgniter's active record implementation is extremely useful. I imagine TinyMVC's is very similar.
The codeigniter website gives the following reason
Beyond simplicity, a major benefit to using the Active Record features is that it allows you to create database independent applications, since the query syntax is generated by each database adapter. It also allows for safer queries, since the values are escaped automatically by the system.
it is an opinion (my)...
you can play with part of the query qithout handling long string.
for example:
if(){
$this->db->where($a,$b);
$this->db->where($c,$d);
$this->db->where($e,$f);
}else{
$this->db->where($g,$h);
$this->db->where($i,$j);
$this->db->where($k,$l);
}
writing the above statement in one string is not very nice coding and hard for maintenance.
And in addition to that, you can than generate cross database queries (helps for migration and in shelf products).
that is only one good reason...
It is not really different, it's more to make it easier for you, the programmer. Because if you write the query yourself, you have to worry about security yourself (mysql injection for example) using their model, they do that for you. You only need to put your variables in and done.
It is way easier to be consistent in your code. And easy to always escape strings etc when doing inserts/updates. Security is important.
<troll>
Because some people are allergic to SQL and want to shoehorn objects into relational operations.
Security ? Use prepared statements. Portability ? Have a list of queries per DBM.
And if you have to dynamically generate a query, you may have to rethink how you separate your database layer from your application layer.
</troll>
The advantage of those ways of doing database queries is all this code can be generated quickly using some framework. Which enables you to prototype applications very fast which you'll need often.
And you don't have to learn vendor specific SQL when going from one DBM to another (google "Oracle Limit" to see an example of what I mean).
I'm pretty new to OOP (and also in PHP programming), and I have some troubles using objects and interfacing them with db data (MySql in my case, but this is irrelevant).
I have on the DB a table USERS. I declared a class User in the PHP code, with the various properties and methods I need. The problem is interfacing this thing to db.
I put code to retrieve data from the db in the constructor, so when I need to work on a specific user, I simply write $user = new User($user_id); and start working with the object.
When I've done I call a method on the object to save data on db, and this works pretty well.
The problem arise when I need data about many users in a single page, as in showing the complete list of users. With this approach, when I need the list of users, I have to instantiate all the objects, and this causes the code to make too many queries of the type SELECT * FROM users WHERE user_id = ..., instead than a single query such as SELECT Only_needed_fields FROM users WHERE 1 LIMIT 20 as I would've done in procedural code.
Which is the correct OOP approach to such a problem? Which is the good-practise alternative to retrieving data in the constructor?
Notice Please, don't suggest using frameworks of any kind - I'd like learn pure OOP PHP before using abstraction layers -
Your problem is that you are not seperating the layers of your application. What you should do is have a factory class instantiating your users that handles the database calls and let the user class only handle the business logic. This will also contribute to the testability of your code.
Your factory class could have two methods. One for loading a single user and instantiating it and another for loading a set of users.
A factory method is always plausible, then you can implement an optimised common query:
list($one, $two) = User::GetMultiple (array( 1, 2 ));
Translating into
SELECT * FROM users WHERE user_id IN ( 1, 2 );
I'm creating a small framework for my web projects in PHP so I don't have to do the basic work over and over again for every new website. It is not my goal to create a second CakePHP or Codeigniter and I'm also not planning to build my websites with any of the available frameworks as I prefer to use things I've created myself in general.
I have had no problems in designing and coding the framework when it comes to parts like the core structure, request handling, and so on, but I'm getting stuck with designing the database interface for my modules.
I've already thought about using the MVC pattern but found out that it would be a bit of an overkill for my rather small project(s).
So the exact problem I'm facing is how my frameworks modules (viewCustomers could be a module, for example) should interact with the database.
Is it (still) a good idea to mix in SQL directly into PHP code? (Would be "old way": mysql_query( 'SELECT firstname, lastname(.....))?
How could I abstract a query like the following?
SELECT firstname, lastname FROM customers WHERE id=X
Would MySQL "helper" functions like
$this->db->customers->getBy( 'id', $x );
be a good idea?
I'm not really sure because they tend to become useless when dealing with more complicated queries like the pretty much trivial one above.
Is the "Model" pattern from MVC my only real option to solve this?
What do you currently use to solve the problems shown above?
I believe you just want to get access to your DB from your module. I'd avoid using mysql_query directly from the code. Rather, going for simple model with abstracted DB access would be easy and straight-forward.
For example, you can have a file like models/Customers.php with this code:
<?php
class Customers {
public function getById($id) {
$sql = "SELECT first_name, last_name FROM customers WHERE id='$id'";
$res = $DB::getRow($sql);
return ($res);
}
}
I am assuming some kind of DB helper is already instantiated and available as $DB. Here is a simple one which uses PDO.
Now, you should include this in your module and use the following way:
<?php
include_once "models/Customers.php";
$customers = new Customers();
$theCustomer = $customers->getById(intval($_REQUEST['cust_id']));
echo "Hello " . $theCustomer['first_name']
Cheers.
have you looked into http://www.doctrine-project.org/ or other php orm frameworks (zend_db comes to mind)?
If you need speed, then use raw queries (but you should really use PDO with prepared queries).
If you want something more OOP, you can —as you suggest it— design this with helpers.
Once, I've designed something similar which had the following concept:
DB connection/handler classes (handling multi-connections to different databases and different servers such as MySQL, Oracle, etc.);
A class per action (ie. SELECT, DELETE, etc.);
Filter classes (eg. RangeFilter);
The code looked something like this:
$select = new Select('field1', 'field2', );
$result = $select->from('myTable')
->addFilter(SQLFilter::RangeFilter, 'field2')
->match(array(1, 3, 5))
->unmatch(array(15, 34))
->fetchAll();
It's a simple example of how you can build it.
You can go further and implements automated handling of table relations, field type check (using introspection on your tables), table and field alias support, etc.
It might seem to be a long and hard work, but actually, it won't take you that much time to make all these features (≈1 month).
Three tips:
Use Stored Procedures (so you can separate the php from the db)
Use PDO/MySQLi for prepared statements CALL NEWS_LIST(?, ?)
Use a Static Class for your DB. Allows you to access it within any module.
Raw SQL is still the winner for me, I like to control what I send to the server (for cases like index usage, complex JOIN clauses and etc) so I generally stay away from helper functions.
You should use PDO which already provides a lot of power and if that's not enough, you can extend that (possibly with your own functions, such as checking for hits on Memcached/APC before actually querying the database). You can also extend the class to implement your own SQL functions like:
function getUser($user_id) {
return $this->query("SELECT * FROM users WHERE id = " . (int) $user_id);
}
Of course that, from the model you should still be able to send:
$this->db->query("SELECT * FROM users WHERE id = " . (int) $user_id);
and get the same result. The functions should act merely as a shortcut and the extended class should not be included with the framework as it will be site-dependant.
The MVC pattern will fit nicely into this because you can use the database merely as a driver and your model can then transform the data into what you need. It's not hard to create a simple MVC structure and it will bring you benefits later.
You sound like me. Have you seen http://github.com/Xeoncross/micromvc and the one file ORM in http://github.com/Xeoncross/database? Dig through my code and I think you will find what you're looking for.
The solution is to use the full raw power of some queries - while still allowing ORM and query builders (like codeigniter's AR) for other things.
Both are good.
Not that i know the definitive answer (nor do i think it exists), but i thought i can just share what i have here. I use my own db 'framework', lightweight (~1000 lines currently) and easy to use. My main goal was to simplify the use of sql, not to 'hide' it from the programmer (me:). Some examples:
// row() is 'query' + 'fetch' in one
$user = $db->row("select * from users where id=25");
// the same, injection safe
$user = $db->row("select * from users where id=?", $_GET['id']);
// ? placeholders are smart
$someUsers = $db->rows("select * from users where id IN(?)", array(1, 2, 10));
// ...and even smarter
$data = array('name' => 'Joe', 'age' => 50);
$id = 222;
$db->exec("update users set ?a where id=?", $data, $id);
// 'advanced' fetch functions
$topNames = $db->vlist("select name from users order by name limit 10");
$arrayOfIds = $db->nlist("select id from users where age > 90");
// table() returns a Table Gateway
$db->table('users')->delete('where id=?', 25);
// yes, this is safe
$db->table('users')->insert($_POST);
// find() returns a Row Gateway object
$db->table('users')
->find('where name=?', 'Joe')
->set('status', 'confirmed')
->save();
Understand this: database interaction is a solved problem.
So unless you really want to do it a) for the experience or b) because you're OCD and want to know every character of the code you'll be using, then I'd choose an existing solution.
And there are many: PEAR::MDB2, Zend::Db, Creole, Doctrine, Propel, just to name a few.
I've just come off the "helper functions" path and the one thing that bugged me was that I continued adding functions in one file which grew and grew with identical or similar or defunct functions. I think the line count was at 600 and that is way to much for a single file in my opinion. This has not put me off the idea but I'll be more organised for the next trek. I'll probably split the db functions into multi files according to the db operation (select, insert etc...).
So my advice is to go try the "helper functions" and be as organized as you can.
Also, I used PDO for the first time and quite liked it. Its not as low tech as the mysql() functions or as bloat tech like some we could mention but won't. I'll be using PDO again.
It seems like there are many different opinions on this topic and as I haven't found a really satisfying answer here yet and the bounty is nearly over, I'll just write what I have come up in the last days after some trial and error:
I'm using a singleton MySQL class to handle the connection and the very basic queries as well as errors that may occur.
Single pages like /users/show/1 (using mod_rewrite) don't use raw SQL but some kind of lightweight ORM that works like in the following example:
$user = $this->db
->users
->getBy( 'id', $id );
$this->db is an instance of a Database Abstraction class with a __get( $tableName ) method. Accessing the undefined users property then triggers it. The rest explains itself; A query is formed from the arguments passed to getBy( ) (SQL escaping is also handled by it) and its results are returned as an array.
I haven't finished the whole idea yet, but adding a new user to the database could look like the following:
$user = $this->db
->users
->new;
$user->id = 2;
$user->name = 'Joe';
$user->save( );
As I said the concept isn't really completed and may have (huge) flaws in it. Yet I think that it may be easier to write, more secure and easier to maintain than plain MySQL.
Some other good sides of the whole "thing" would be that it is small, therefore rather fast and also pretty straightforward.
I know that this can't compete with the extremely powerful ORMs and frameworks already out there but I'm still creating this for some reasons mentioned in one of my comments above.
If you do plan on making a database class it may be an idea looking into making it a singleton, allowing you to use it without declaring it/creating it, as...
global $db;
$db = new db;
$db->query ('... sql ...');
is kinda redundant when you can do
db::query ('... sql ...');
I have a set of SQL functions that I use on a near regular basis to reduce what used to be a multi-line escaped lot of SQL to a single call, for example:
get_element ($table, $element, $value, $column='id');
get_row ($table, $value, $column='id');
So if you just want to get the name from a table 'customers' where the id is 4 you:
$name = db::get_element ('customers', 'name', 4);
There are also accompanying functions query_element and query_row, where you just pass it an SQL string and it returns a single element/row.
Along with the functions for insert/update, e.g.
$array = array (
'name' => 'bob jones',
'age' => 28
);
$insert_id = db::insert_array ('customers', $array);
$customer_details = db::get_row ('customers', $insert_id);
$customer_details['age'] = 30;
db:update_array ('customers, $customer_details);
Would create a new row, pull the details back out, update the age, then re-write it to the database.
Creating custom SQL access modules on a per-table basis is generally a mistake I have always found - it's better to just generically query the database using sensible functions.
If you do have to use anything with complex joins then it is always best to create function for it getCustomerInfo () for example, but if you just want a generic table value lookup making lots of custom methods just increases the chances of mistakes in one of them. Plus escaping data is very important - if you can use non-sql as much as possible and funnel it through a few core functions you can ensure everything is properly escaped fairly easily.
If you want to look at my custom database class let me know.