473,405 Members | 2,176 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes and contribute your articles to a community of 473,405 developers and data experts.

Creating a Data Abstraction Layer in PHP

Motoma
3,237 Expert 2GB
This article is cross posted from my personal blog. You can find the original article, in all its splendor, at http://motomastyle.com/creating-a-my...-layer-in-php/.

Introduction:
The goal of this tutorial is to design a Data Abstraction Layer (DAL) in PHP, that will allow us to ignore the intricacies of MySQL and focus our attention on our Application Layer and Business Logic. Hopefully, by the end of this guide, you will have a working DAL and learn a little about PHP, MySQL, and Object-Oriented design in the process.

Assumptions:
You know PHP and have is set up.
You know MySQL and have it set up.
You have a cursory knowledge of object-oriented methodologies.
You are not a smacktard.

Defining the Project:
The purpose of this project is to create a working MySQL DAL to distance ourselves from the menial tasks that are associated with PHP/MySQL systems, such as passing around connection handles and iterating through result sets. We will create an object which will create and maintain our connection to our database, provide us with the tools necessary to perform all of our required SQL queries, properly handle errors, tidily present our data, and keep our application code clean.

Defining the Object:

Our Database object will consist of local variables, a constructor, mutators, and methods. I will leave out destructor and scoping from the database class, in order to maintain compatibility with PHP4.

Local Variables:
We will need variables to keep track of our connection information, as well as keep track of any open connections we may have.
The Constructor:
The Constructor for our class will server to create an instance of our Database class. It will provide the class' variables with their initial values.
The Mutators:
Our class will provide the functionality for changing databases on the fly, therefor we will design it to allow us to alter our connection information, both individually, and as a whole.
The Methods:
We are designing our DAL to connect to a database, therefor our Database class better have a connect method! Along with connect method, it should have a way of disconnecting, performing queries, and returning results.

Object Parts:

Our Database class wills start off simple enough; we need only to define the class and our variables to get the ball rolling:
Expand|Select|Wrap|Line Numbers
  1. class Database
  2. {
  3.     var $database_name;
  4.     var $database_user;
  5.     var $database_pass;
  6.     var $database_host;    
  7.     var $database_link;
  8.  
Next we move on to our constructor. Remember, this will give us the initial values for our database connection, so it is fine if we enter our current information as the default:
Expand|Select|Wrap|Line Numbers
  1.     function Database()
  2.     {
  3.         $this->database_user = "motoma";
  4.         $this->database_pass = "bI1dU5";
  5.         $this->database_host = "localhost";
  6.         $this->database_name = "dalDatabase";
  7.     }

Now we move on to our mutators. These will allow us fine-tuned control over our connection data. We will want one method for each variable we want to set. We could also, optionally, create one to set all in one call:
Expand|Select|Wrap|Line Numbers
  1.     function changeUser($user)
  2.     {
  3.         $this->database_user = $user;
  4.     }
  5.  
  6.     function changePass($pass)
  7.     {
  8.             $this->database_pass = $pass;
  9.     }
  10.  
  11.     function changeHost($host)
  12.     {
  13.         $this->database_host = $host;
  14.     }
  15.  
  16.     function changeName($name)
  17.     {
  18.         $this->databse_name = $name;
  19.     }
  20.  
  21.     function changeAll($user, $pass, $host, $name)
  22.     {
  23.         $this->database_user = $user;
  24.         $this->database_pass = $pass;
  25.         $this->database_host = $host;
  26.         $this->database_name = $name;
  27.     }

We are really on a roll now! On to our connection related methods. Our connect method will need to establish a connection using the credentials we provide, and let us know when connection errors occur:
Expand|Select|Wrap|Line Numbers
  1.     function connect()
  2.     {
  3.         $this->database_link = mysql_connect($this->database_host, $this->database_user, $this->database_pass) or die("Could not make connection to MySQL");
  4.         mysql_select_db($this->database_name) or die ("Could not open database: ". $this->database_name);        
  5.     }

Conversly, the disconnect method will close any connection our class has previously made:
Expand|Select|Wrap|Line Numbers
  1. function disconnect()
  2.     {
  3.         if(isset($this->database_link)) mysql_close($this->database_link);
  4.         else mysql_close();    
  5.     }

That was not very dificult, was it? On to our methods. The first, and most basic thing we will need to do is perform a query which returns no results. DELETES, UPDATES, and INSERTS fall into this category of query. Before we can perform a query, however, we should check if the database connection has been established. If not, we will need to establish a connection before we perform our query:
Expand|Select|Wrap|Line Numbers
  1.     function iquery($qry)
  2.     {
  3.         if(!isset($this->database_link)) $this->connect();
  4.         $temp = mysql_query($qry, $this->database_link) or die("Error: ". mysql_error());        
  5.     }

Onto our first result returning query handler. Personally I like working with arrays. Associative arrays make sense when returning a result set, due to the structures in which we commonly see these results displayed. Much like our resultless query handler, we will need to check on our connection before we try anything. It will then perform the query, and iterate through the result set, placing it tidily in an associative array for us to work with.
Expand|Select|Wrap|Line Numbers
  1. function query($qry)
  2.     {
  3.         if(!isset($this->database_link)) $this->connect();
  4.         $result = mysql_query($qry, $this->database_link) or die("Error: ". mysql_error());
  5.         $returnArray = array();
  6.         $i=0;
  7.         while ($row = mysql_fetch_array($result, MYSQL_BOTH))
  8.             if ($row)
  9.      $returnArray[$i++]=$row;
  10. mysql_free_result($result);
  11.         return $returnArray;
  12.     }
  13. }

One could add a few more functions to this list, namely for returning the last inserted id of an auto_increment field and checking for table existence.

Using our DAL:
Now that we have a DAL, how do we use it? Here's an example:
Expand|Select|Wrap|Line Numbers
  1. <?php
  2. // Our Database Class
  3. include("lib/database.php");
  4.  
  5. // Instantiate our Database Class
  6. $db = new Database();
  7. // Query!
  8. $resArr = $db->query("SELECT * FROM users WHERE userid != 1");
  9.  
  10. // $resArr is now an associative array containing the entire result set
  11. // of the SQL query. We can now use and abuse this data as necessay.
  12. // In this hypothetical situation, I am returing all of the users in
  13. // my table except the Administrator
  14.  
  15. echo '<table><tr><th>UserID</th><th>UserName</th>';
  16. echo '<th>RealName</th><th>Homepage</th></tr>';
  17. foreach($resArr as $user)
  18. {
  19.     echo '<tr><td>'.$user['userid'].'</td><td>'.$user['username'].'</td>';
  20.     echo '<td>'.$user['realname'].'</td><td>'.$user['homepage'].'</td></tr>';
  21. }
  22. echo "</table>";
  23. ?>

Wrap Up:
Hopefully this tutorial has shown you how to quickly and easily create a minimal Data Abstraction Layer for your web application. Having a DAL to work with allows us to focus on the Application and Business layers of our applications, without having to go through the monotony of data access.
Apr 14 '07 #1
17 46423
nathj
938 Expert 512MB
I'm new to web development but with a background in OO desktop application development. This tutorial has sown me exactly what I needed to know. I can now get what I want from my web system. This was brilliantly clear

Thank you
Nathan
Jun 15 '07 #2
Motoma
3,237 Expert 2GB
I'm new to web development but with a background in OO desktop application development. This tutorial has sown me exactly what I needed to know. I can now get what I want from my web system. This was brilliantly clear

Thank you
Nathan
Glad it helped you out. Good luck with your projects, and come back to TSDN any time you have a question.
Jun 15 '07 #3
realin
254 100+
Glad it helped you out. Good luck with your projects, and come back to TSDN any time you have a question.

wow that is something i have been looking for :)
thanks mann :p
Aug 5 '07 #4
kovik
1,044 Expert 1GB
Well, I guess I'll have to be the critic then.

This database wrapper class is extremely simplistic... Almost too much so. I'm not sure if this is as far as you have gone with this, as it is very possible that you were giving a very simplistic example, but I feel that I should at least extend upon it for people who have not written a database wrapper before.

We (and by we, I mean the DevNetwork community) have found that the database wrapper should handle nearly everything that the database will do. We give it a query, it handles the rest. However, it doesn't do all of this in one function... That drastically lowers its flexibility.

There is one query function. This function performs the query, and then saves the result if it is a valid resource using is_resource(). SELECT statements create valid resources. UPDATE statements do not. Whenever a result set is created, we save it. The result itself is an entirely different object, and can be handled on its own.

Getting the rows of a result object is handled in the results object with different fetch functions like FetchRow(), FetchObject(), etc. We also give shortcuts to these functions in the database class that automatically uses the last result set object (which should be saved in the database object).

The way the database objects works is like this:

Expand|Select|Wrap|Line Numbers
  1. $pSql->Query("SELECT * FROM `table`;");
  2.  
  3. while($data = $pSql->FetchObject())
  4. {
  5.     print_r($data);
  6. }
If you need to handle queries that don't return a results set:

Expand|Select|Wrap|Line Numbers
  1. $pSql->Query("SELECT * FROM `table`;");
  2. $pSql->Query("INSERT INTO `table` SET `foo` = 'bar';"); // Doesn't return a results set
  3.  
  4. // Still using the data from the original SELECT statement
  5. while($data = $pSql->FetchObject())
  6. {
  7.     print_r($data);
  8. }
And if you need to handle multiple queries that DO return results sets:

Expand|Select|Wrap|Line Numbers
  1. $pSql->Query("SELECT * FROM `table`;");
  2. $pResults = $pSql->GetResults();
  3.  
  4. $pSql->Query("SELECT * FROM `anotherTable`;");
  5. $pResults2 = $pSql->GetResults();
  6.  
  7. while($data = $pResults->FetchObject())
  8. {
  9.     print_r($data);
  10.  
  11.     if($data2 = $pResults2->FetchObject())
  12.     {
  13.         print_r($data2);
  14.     }
  15. }
We've had a few debates on database wrappers and the goal is flexibility and capability. The flexibility comes from not restricting yourself to something like handling everything through the Query() function or not saving your results sets as separate entities in case you have other plans for them, like sending the resource to different functions. The capability comes from things like saving the errors, getting the last insert id, determining how many rows were affected, determining how many rows were matched (which is different than affected!), getting stats, explaining queries, saving the amount of queries performed, timing queries, etc.


Also, a personal preference of mine is to refrain from saving the username, password, or database name inside of the database class. You need to reconnect at every page request anyway, and when you serialize objects across sessions and such, it will attempt to serialize the database connection as well, leaving your credentials stored in plain text.

Another preference of mine is to use exceptions in the query class that save the mysql_error() message along with the erroneous query, and then just run all of my database queries through the same try...catch statement, and whenever an exception is caught, it logs the error message for me and tells the user that there was an error processing their request.


I'm not sure how long you've been using yours, so I don't want to seem rude or anything. My first attempt at a database wrapper was similar to this. However, just like all of my other classes, it developed through necessity. I re-factored as I found that I needed more abilities, more efficiency, more security measures, and cleaner usage methods so that the resulting code was as short and sweet as I could make it.
Aug 8 '07 #5
nathj
938 Expert 512MB
I re-factored as I found that I needed more abilities, more efficiency, more security measures, and cleaner usage methods so that the resulting code was as short and sweet as I could make it.

This is something that is always worth doing. I have taken the basic DAL that is described in this article, and as a newbie at the time it was fantastic. It really helped me get to grips with PHP as an OO language. I have since tweaked my DAL to handel some of the issues you mention in your post.

As a starting point I think the DAL listed here is a great way to get going. But we should all remember that however good we think something we've developed is there is almost always room for improvement, but we've got to start somewhere.

Cheers
nathj
Aug 13 '07 #6
pbmods
5,821 Expert 4TB
Now that PHP 4 has been discontinued, shall we update this article for PHP 5?
Aug 26 '07 #7
Markus
6,050 Expert 4TB
OOP scares me :(

I've yet to delve into it..
Mar 11 '08 #8
coolsti
310 100+
I think this is a very good How To and I feel it is very good that it is as simple as it is, in order to be clear to people who have not done much OOP before. Of course it can be made more complex for more flexibility and functionality, as one poster mentioned, but as a tutorial on the use of OOP, it is excellant.

I have not yet myself gone so far as to make a class to handle my database queries, but have made a lot of functions to simplify things. The beauty of making a class is that everything becomes well organized and easy to find (i.e. no more "where is that function that did that database select for me, I can't remember....") and namespaced. But I have made a lot of classes that do a lot of other stuff for me, and this tutorial will maybe push me to make one for databases.

However, I believe that one can also go too far with OOP programming in a flexible scripting type language like PHP. For me, my application is so database oriented, that each and every page requires a database connection, so I don't need a class to create and maintain this for me. The connection is made early on in the scripts and the connection is maintained until the script ends.

Another issue I have is whether it is important or not to have mutators. I started off writing lots of these for my classes, but then dropped them. PHP is too flexible; if I need to change the value of a class variable, I change it directly rather than call a function to change it for me (public vs. private variables). What I am changing and the fact that it belongs to a class is obvious from the code. Of course a mutator would be useful if you want to carry out an associated task along with changing the variable in question, like logging the variable change.

But it is a great How To and tutorial. Programming with a bit of OOP like this simplifies greatly the task of programming PHP.
May 6 '08 #9
DaWei
1
If you don't use mutators it's a good bet that you're a one-man show writing relatively small applications that aren't widely distributed, modified, and maintained.
May 8 '08 #10
bilibytes
128 100+
You could also explain the Factory Pattern an show how to implement it on DAL point of view.
Jan 17 '09 #11
Dormilich
8,658 Expert Mod 8TB
wouldn't be the Singleton Pattern be the first pattern discussed, when it comes to database handling?

btw. what use shall the Factory Pattern have, concerning DAL (that's not clear to me)
Mar 4 '09 #12
pbmods
5,821 Expert 4TB
@Dormilich
DBAL does work much better with the singleton pattern, as maintaining multiple database connections per script execution tends to cause more problems while trying to solve a problem that doesn't exist.

The factory pattern is often useful in situations where you might be using different database adapters. Generally this is only useful for one-size-fits-all frameworks such as Zend Framework:

Expand|Select|Wrap|Line Numbers
  1. $db = Zend_Db::factory('Pdo_Mysql', array(
  2.     'host'     => '127.0.0.1',
  3.     'username' => 'webuser',
  4.     'password' => 'xxxxxxxx',
  5.     'dbname'   => 'test'
  6. ));
-- from http://zendframework.com/manual/en/zend.db.html

In the code above, Zend_Db::factory() takes care of loading the appropriate adapter class and satisfying any platform-specific dependencies.

A database class is not the best example of a situation calling for the factory pattern because the type of adapter does not generally change that much.

In other words, if you have data for the same application stored on two different database systems, you're probably doing it wrong.

A data transfer class, for example, might use a factory to load the appropriate class depending on the protocol of a given URL. E.g.:

Expand|Select|Wrap|Line Numbers
  1. $client = Data_Transfer_Client::factory($url);
  2. // $url = 'http://...' => returns Data_Transfer_Client_Http
  3. // $url = 'smb://...' => returns Data_Transfer_Client_Samba
  4. // etc.
  5.  
And so on. Because the value of $url is so volatile, it is much easier to write a factory method and put all one's if statements in a single location.
Mar 5 '09 #13
azegurb
32
here
i dont understand what does this function do.

i try to undertand of loop objective but nothink i understood.
can you explain me for what reason there is loop used and what is a purpose to create this array

thanks beforehands
Expand|Select|Wrap|Line Numbers
  1. function query($qry) 
  2.     { 
  3.         if(!isset($this->database_link)) $this->connect(); 
  4.         $result = mysql_query($qry, $this->database_link) or die("Error: ". mysql_error()); 
  5.         $returnArray = array(); 
  6.         $i=0; 
  7.         while ($row = mysql_fetch_array($result, MYSQL_BOTH)) 
  8.             if ($row) 
  9.      $returnArray[$i++]=$row; 
  10. mysql_free_result($result); 
  11.         return $returnArray; 
  12.     } 
  13. }
May 6 '10 #14
Dormilich
8,658 Expert Mod 8TB
this method does a query and immediately fetches the result into an array (you’ll probably have more than one result row)
May 6 '10 #15
azegurb
32
Pls can you show it in a simple real wordl example
thank you very much
May 6 '10 #16
Markus
6,050 Expert 4TB
The article shows you a clear example of how to use the class.
May 9 '10 #17
azegurb
32
Ok thank you very much. i have already understood
May 9 '10 #18

Sign in to post your reply or Sign up for a free account.

Similar topics

1
by: 11abacus | last post by:
Hi, Just wondering if any of you has thought about or is working on a payment gateway abstraction layer (PEAR-style)? I'm interested on developing that or at least start a discussion about...
5
by: David Winter | last post by:
I am looking for a "WYSIWYG" style, browser-based editor for (X)HTML content. It should (at least) run under Windows, preferably in Mozilla/Firefox, but IE would be OK, too. Now I know there are...
30
by: Luke Wu | last post by:
Hello, >From spending some time in clc, I've come to realize that C's model of the CPU can be totally different from the atual CPU. Is it safe to say that almost nothing can be gleaned about...
2
by: Eric Cathell | last post by:
I am using dOOdads as my persistence layer. I have a business object Person that sits in a Project called DOMAIN I have 2 dOOdads in a project called BLL. one is _Person and is declared...
3
by: S. Lorétan | last post by:
Hi guys, I'm coding an application connected to a database. I have some clients that use this program with 2 or 3 computers, and I use MsSQL Express for them. But if the client needs more...
13
by: Jeff | last post by:
Hey ASP.NET 2.0 I'm designing the DAL (Data Access Layer) of my web application. I want every table to have a strongly typed object as wrapper arround the table. So that for example if the...
13
by: LordHog | last post by:
Hello all, I have a little application that needs to poll a device (CAN communications) every 10 to 15 ms otherwise the hardware buffer might overflow when there are message burst on the bus. I...
7
by: =?Utf-8?B?U2F2dm91bGlkaXMgSW9yZGFuaXM=?= | last post by:
Why separate the Data Access Layer (DAL) methods from the Business Layer (BL)methods of an object ? Why not have them inside the object itself, since it's going to save its own data, using its own...
2
bilibytes
by: bilibytes | last post by:
Hi there, I am wondering what are the advantages of having a database abstraction layer like the one explained Here (in Devshed) Don't you get more efficient queries writing them down in SQL? ...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.