So I'm trying to adopt good object oriented programming techniques with PHP. Most (read all) of my projects involve a MySQL database. My immediate problem deals with the users model I need to develop.
My current project has Agents and Leads. Both Agents and Leads are Users with much of the same information. So, obviously, I want a class Agents and a class Leads to extend a common class Users. Now, my question is as follows:
How should the SQL best be handled for loading these objects? I don't want to execute multiple SQL statements when I instantiate an Agent or a Lead. However, logic tells me that when the Users constructor is fired, it should execute a SQL statement to load the common information between Agents and Leads (username, password, email, contact information, etc). Logic also tells me that when the Agents or Leads constructor is fired, I want to execute SQL to load the data unique to the Agents or Leads class....But, again, logic also tells me that it's a bad idea to execute 2 SQL statements every time I need an Agent or Lead (as there may be thousands of each).
I've tried searching for examples of how this is generally handled with no success...Perhaps I'm just searching for the wrong thing?
You basically have three approaches to this problem (one of which I'll eliminate immediately):
One table per class (this is the one I'll eliminate);
A record type with optional columns; and
A record type with a child table depending on type that you join to.
For simplicity I generally recommend (2). So once you have your table:
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
type VARCHAR(10),
name VARCHAR(100)
);
where type can be 'AGENT' or 'LEAD' (for example). Alternatively you can use one character type codes. You can then start to fill in the blanks with the object model:
You have a User parent class;
You have two child classes: Lead and Agent;
Those children have a fixed type.
and it should fall into place quite easily.
As for how to load in one statement, I would use some kind of factory. Assuming these barebones classes:
class User {
private $name;
private $type;
protected __construct($query) {
$this->type = $query['type'];
$this->name = $query['name'];
}
...
}
class Agent {
private $agency;
public __construct($query) {
parent::constructor($query);
$this->agency = $query['agency'];
}
...
}
class Lead {
public __consruct($query) {
parent::constructor($query);
}
...
}
a factory could look like this:
public function loadUserById($id) {
$id = mysql_real_escape_string($id); // just in case
$sql = "SELECT * FROM user WHERE id = $id";
$query = mysql_query($sql);
if (!query) {
die("Error executing $sql - " . mysql_error());
}
if ($query['type'] == 'AGENT') {
return new Agent($query);
} else if ($query['type'] == 'LEAD') {
return new Lead($query);
} else {
die("Unknown user type '$query[type]'");
}
}
Alternatively, you could have the factory method be a static method on, say, the User class and/or use a lookup table for the types to classes.
Perhaps polluting the classes with the query result resource like that is a questionable design in the strictest OO sense, but it's simple and it works.
Will you ever have a user that's not a Lead or Agent? Does that class really need to pull data from the database at all?
If it does, why not pull the SQL query into a function you can override when you create the child class.
Could you not inherit say a skeleton of the SQL, then use a function in each sub-class to complete the query based on its needs?
Using a really basic example:
<?php
//our query which could be defined in superclass
$query = "SELECT :field FROM :table WHERE :condition";
//in our subclass
$field = "user, password, email";
$table = "agent";
$condition = "name = 'jim'";
$dbh->prepare($query);
$sth->bindParam(':field', $field);
$sth->bindParam....;//etc
$sth->execute();
?>
As you can see my example isn't amazing, but should allow you to see what I am getting at. If your query is very similar between subclasses then I think my suggestion could work.
Obviously it will need some tweaking but it is probably the approach I would take.
Related
I may no be asking this questions right but here goes... I have a database with 2 tables "users" (for users name/password/etc) and "usersInfo" (users first name/last/address/etc). I only have 1 Users.php class- do i need 2 separate classes to create 2 objects from to hold the "users" & "usersInfo" data for the same user or will 1 class work (and still make 2 objects?)?
some of my Users.php class/
public function __construct($user = null) {
$this->_db = DB::getInstance();
$this->_sessionName = Config::get('session/session_name');
$this->_cookieName = Config::get('remember/cookie_name');
if(!$user) {
if(Session::exists($this->_sessionName)) {
$user = Session::get($this->_sessionName);
if($this->find($user) || $this->findUserInfo($user)) {
$this->_isLoggedIn = true;
} else {
//logout
}
}
} else {
$this->find($user);
$this->findUserInfo($user);
}
}
public function find($user = null) {
if($user) {
$field = (is_numeric($user)) ? 'id' : 'username';
$data = $this->_db->get('users', array($field, '=', $user));
if($data->count()) {
$this->_data = $data->first();
return true;
}
}
return false;
}
public function findUserInfo($user = null) {
if($user) {
$test3 = $this->_db->get('users', array('username', '=', $user));
$userId = $test3->first()->id;
$data2 = $this->_db->get('usersInfo', array('user_id', '=', $userId));
if($data2->count()) {
$this->_userInfoData = $data2->first();
return true;
}
}
return false;
}
public function data() {
return $this->_data;
}
public function userInfoData() {
return $this->_userInfoData;
}
Currently I have to create 2 objects to use all the data i need for the same user.
for example, in on of my pages.php i have:
$user = new User();
$user1 = new User($user->data()->username);
$userNane = $user->data()->username; //holds users username form "users" table
$userName1 = $user1->userInfoData()->first_name; // holds users first name from "usersInfo" table
It works but doesnt look right... is it efficient/ok practice/etc. If not, suggestions?
Also, first post, take it easy :)
What is the relationship between the 2 tables?
1-1 ? 1-*? Can a user have multiple persona? Can a persona correspond to many user accounts? Will this relation change in the future?
Depending on your answers, you might see which solution fit better your plans.
1-1 relation: you can afford to have a single class to hold related records. It will be easier to manage from the perspective of your application
Otherwise, you'll need at one time or another to handle a record separately from related records in the other table. You'll be better off with 2 distincts objects.
if you plan to change things later on for the second situation, you should keep things as they are.
In the specific case of user data, your comment bring the insight that certain data are more sensitive than others. In retrospect, I guess that's the reason you made these two tables separate. From that point of view, it is certainly better to keep both objects separate, even in a 1-1 relationship.
Regarding your code, indeed, having a dedicated UserInfo class, rather than piggy backing on another instance of User, would clearly be a good thing. A very important idea of good design is separation of concerns: you want each class to handle one and only one purpose, so that any modification to a class will have a limited scope of impact on the rest of the code.
As an example, the only thing you need to retreive a userinfo row, and therefore construct an object wrapping it, is the user id. Instead of delegating the whole job to a method of User, I would probably extract the iout of the User instance, and pass it to the adhoc UserInfo constructor or static method: there, each class only deals with things in its own perimeter. Of course, findUserInfo could also delegate to that same function.
IMO, on of the most important steps in designing/developing an app is creating a sound schema and model. Not sure how much database design experience you have, but you will want to read up on First Normal Form (1NF), and eventually (2NF and 3NF).
Part of the schema design stage is to identify all the nouns which you will reference in your app, in your case a user is a perfect example. Each of these identified nouns will then have attributes, which you will want to consider, in how each will be stored.
The problem in your situation is that you have user and user_info. As you stated user is for name, password, etc, whereas user_info is for first_name, last_name, address etc. Part of the design stage is to determine which of these attributes are directly attributable to the user object, and which are more ancillary in nature. Using your example: name, password, first_name, last_name are each directly attributable to the user noun (object), however address is more ancillary in nature, and there may be more than one address per user (billing address, vs physical address), so you may want to consider adding a user_address table. As you can see, by logically separating the attributes of the user noun (object), you start to identify relationships which make more sense (user, user_address) vs (user, user_info).
Once you identify the nouns, and separate their attributes, you can create your schema. From your schema you can use an Object Relational Mapper (ORM) like Doctrine, which will introspect your schema, and generate objects for you to use throughout your app. In your example you would end up with two objects; User and UserAddress. Also it's important that when developing your schema that you identify relationships between tables by implementing a foreign key constraints. For example, your user_address table should have a user_id column, which links to your user table. This way when doctrine introspects your schema, it will also identify these relationships, which makes coding much easier.
Once you have your ORM in place, you can then make code references like this:
// In your controller
$this->user = UserTable::findById($_SESSION['user_id']);
// Then in your view
Welcome <?php echo $user->getFirstName() ?>, to our wonderful app.
We have your addresses listed as follows:
<?php foreach ($user->getUserAddress() as $userAddress) ?>
<div>
<?php echo $address->getStreet() ?>
</div>
<?php endforeach ?>
Yes, it's a very simplistic example, but should properly demonstrate that if you design your schema properly, the code becomes semantic, which makes it easier to write, and maintain.
Here is situation.... ...
I have a DBManager, which is implement a DBInterface, in the DBInterface, I got 4 method:
-create(DBCmd);
-read(DBCmd);
-update(DBCmd);
-delete(DBCmd);
The DBCmd object is responsible for generate the SQL statement, and the DBCmd requires an object in sql statement:
class DBCmd{
public _constructor($aObj){
}
public executeCreate(){
}
public executeRead(){
}
public executeUpdate(){
}
public executeDelete(){
}
}
The flow will be like this:
aObject ---> put it into DBCmd ----> put the DBCmd in DBManager ---> execute
But the problems happen when I get some objects related to other tables, for example...a customer have a purchase record, and which purchase record have many items....
So, what do I do in my read method? should I read all the records related to the customer?? Do I need to loop all the items inside the purchase record too?
If yes, when I doing read customer, I need to query 3 tables, but some that may not need to see.....it waste the resource...
And I come up with another solution, I make a new set of DBCmd, that allow me to get the related DB items, for example:
class getReleatedPurchaseRecordDBCmd{
public _constructor($aCustomerObject){
}
//.... ....
}
But in this "solution", I got some problems, is I loss the relationship in the object customer...yes, I can read back all the records, get the customer object basically don't know any things about the purchase record....
Some may ask me to do something like this:
class customer{
//skip other methods...
public getPurchaseRecords(){
//query the db
}
}
It works, but I don't want the object structure have some strong relationship between the db....That's why I come up with the DBCmd stuff...
So, everything seems to be very coupling, how can solve it? Thank you.
for stuff like this i tend to get the count of sub objects with the initial query usually involving sql COUNT and JOIN, then have a seperate getSubObjects command that can be called if needed later. So for example:
$datamodel->getCustomer($id);//or some such method
returns
class Customer{
$id = 4;
$recordCount = 5;
$records = null;
}
I can then use the count for any display stuff as needed, and if i need the records populated call:
$customer->records = $datamodel->getCustomerRecords($customer->id);
I have these two database tables:
locations
id
name
users
id
location_id
last_name
first_name
I also have User and Location class, they both extends Model and contain some custom methods. For example, there is a get_full_name() method in the User class.
I am using the following codes to load the data,
$this->db->select('users.id, locations.name as location_name, users.last_name, users.first_name');
$this->db->from('users');
$this->db->join('locations', 'users.location_id = location.id');
$query = $this->db->get();
$users= $query->custom_result_object('User'); //now $users is an array of User object
The custom_result_object is a built-in but undocumented function in Codeigniter. It accepts a string of class name and will use it to create objects of that class.
With the above codes, I can access the data like this,
foreach($users as $user)
{
echo $user->id;
echo $user->get_full_name();
echo $user->location_name;
}
But as you can see, location_name is now a field of the User object, what I want is the User object has a field of Location object that allows me to use it like this,
foreach($users as $user)
{
echo $user->id;
echo $user->get_full_name();
echo $user->location->name; // Location is an object
}
I don't want to use DataMapper ORM or any other ORMs, and would also like to avoid the N+1 query performance issue.
How can I do this with minimal amount of codes?
Note: I made up this example just for the demonstration purpose, in my real case, there are quite a lot of tables and classes (which has custom methods defined on them).
Many thanks to you all.
Have you considered the following?
class User {
var location;
public function location()
{
if ( empty($this->location) )
{
$ci =& get_instance();
$this->location = $ci->db->get_where('locations', array('id' => $this->location_id))->row();
}
return $this->location;
}
}
This way, you completely avoid the overhead of loading the location's table unless you need the data, in which case you cache it inside the object for future use.
I had questioned the same for myself but found the simplest way was to iterate through each result (in your case, user) and select the corresponding child (location) and set to the field in the parent object (user->location).
The only alternative way I can think of would involve editing (or extending and creating your own db set) the db_result class to use getters/setters instead of direct fields - perhaps with call_user_func_array(). At that point, you could code your setId() function in the user model to set the $location field upon receiving the userId/foreign key.
get location id from table user, use it's number to grab from table locations by id a object. and put in it.
I'm kind of proud not to accept concepts if there is no good reason. But I'm in doubt about using active record pattern. Currently I'm using zend but say code Igniter has active record.
I dont use.Because
sql is sql it has own syntax.
you can copy to sql editor and it works (if it is working!)
you dont learn another syntax
you dont need to kill your script to gather if active record is writing sql the way you expected
but active record has
you pretend writing like objective php.
When you need to move another db(oracle>mysql :p), you dont need to change rand function to random, active record can make it for you.
does active record have much more capability that I am missing? Can you give some example cases where active record could be a life saver?
An ActiveRecord is
An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data.
This is not what CodeIgniter uses. CI's AR is a basic query object.
The main benefit of an ActiveRecord is it's simplicity. If your application is mainly doing simple CRUD operations and your Table structure matches the ActiveRecord very closely, then it's a good choice. It's easy to abstract CRUD in that case. And you can still add handcrafted SQL to it for certain more complex row manipulations.
class User
{
protected static $dbAdapter;
protected $username;
…
public static function findById($id)
{
$result = self::$dbAdapter->query(
sprintf('SELECT * FROM users WHERE id = %d', $id)
);
if($result) {
return new User($result);
}
}
public function create()
{
try {
return self::$dbAdapter->query(
sprintf(
'INSERT into users …',
$this->username,
…
);
);
} catch …
}
…
}
You'd use that in your application like this:
$john = User::findById(123);
echo $user->username; // John
$jane = new User(array(
'username' => 'Jane'
));
$jane->create();
You definitely don't want to use ActiveRecord if the your rows and the AR don't match closely. AR is an object representing a database row. AR couples the object design to the database design. AR is not an ORM. Trying to put that into it is not practical. If you find you are in need of more juicy Domain Models, you won't be happy with it, because it will ultimately hamper your development due to object-relational impedance mismatch.
Additional readings:
Kore Nordmann: Why Active Record sucks
Bill Karwin: ActiveRecord does not suck
Well this is a simple design question I've wondered about many times and never found a satisfying solution for. My example is with php-sql, but this certainly applies to other languages too.
I have a small database table containing only very few entries, and that almost never needs updating. eg this usertype table:
usertype_id (primary key) | name | description
---------------------------+------------+-------------------
1 | 'admin' | 'Administrator'
2 | 'reguser' | 'Registered user'
3 | 'guest' | 'Guest'
Now in the php code, I often have to check or compare the type of user I'm dealing with. Since the user types are stored in the database, I can either:
1) Select * from the usertype table at class instantiation, and store it in an array.
Then all the ids are available to the code, and I can do a simple select to get the rows I need. This solution requires an array and a db query every time the class is instantiated.
$query = "SELECT info, foo FROM user WHERE usertype_id = ".$usertypes['admin'];
2) Use the name column to select the correct usertype_id, so we can effectively join with other tables. This is more or less equivalent to 1) but without needing to cache the whole usertype table in the php object:
$query = "SELECT info, foo FROM user JOIN usertype USING (usertype_id) WHERE usertype.name = 'admin' ";
3) Define constants that match the keys in the usertype table:
// As defines
define("USERTYPE_ADMIN",1);
define("USERTYPE_REGUSER",2);
//Or as class constants
const USERTYPE_ADMIN = 1;
const USERTYPE_REGUSER = 2;
And then do a simple select.
$query = "SELECT info, foo FROM user WHERE usertype_id = " . USERTYPE_ADMIN;
This is probably the most resource-efficient solution, but it is bad to maintain, as you have to update both the table and the code if you need to modify something in the usertype table..
4) Scrap the usertype table and only keep the types in the php code. I don't really like this because it lets any value get into the database and get assigned to the type of user. But maybe, all things considered, it isn't so bad and i'm just complicating something that should be simple..
Anyways, to sum it up the solution I like most is #2 because it's coherent and with an index on usertype.name, it can't be that bad. But what I've often ended up using is #3, for efficiency.
How would you do it? Any better solutions?
(edit: fixed query in #2)
I would suggest #3 to avoid useless queries, and prevent risk of behavior changes if existing DB table rows are incidentally modified:
Adding the necessary constants in the model class:
class Role // + use namespaces if possible
{
// A good ORM could be able to generate it (see #wimvds answer)
const ADMIN = 1;
const USER = 2;
const GUEST = 3;
//...
}
Then querying like this makes sense:
$query = "SELECT info, foo FROM user WHERE role_id = ".Role::ADMIN;
With an ORM (e.g. Propel in the example below) you'll end up doing:
$isAdminResults = UserQuery::create()->filterByRoleId(Role::ADMIN);
I almost always go for option 3). You could generate the code needed automatically based on what is available in the DB. The only thing you have to remember then is that you have to run the script to update/rewrite that info when you add another role (but if you're using phing or a similar build tool to deploy your apps, just add a build rule for it to your deploy script and it will always be run whenever you deploy your code :p).
Why not denormalize the DB table so instead of having usertype_id, you'd have usertype with the string type (admin). Then in PHP you can just do define('USERTYPE_ADMIN', 'admin');. It saves you from having to modify two places if you want to add a user type...
And if you're really worried about any value getting in, you could always make the column an ENUM data type, so it would self manage...
For tables that will contain "type" values especially when is expected such table to change over time I tend to use simple approach:
Add Varchar column named hid (comes from "human readable id") with unique key. Then I fill it with id meaningful to humans like:
usertype_id (primary key) | name | description | hid (unique key)
---------------------------+------------+-------------------+---------------
1 | 'admin' | 'Administrator' | 'admin'
2 | 'reguser' | 'Registered user' | 'user'
3 | 'guest' | 'Guest' | 'guest'
When you need the actual id you will have to do select based on hid column, i.e.
select usertype_id from tablename where hid = "admin"
This is not an efficient approach but it will ensure compatibility of your application among different deployments (i.e. one client may have 1.admin, 2. guest; other client 1.admin, 2. user, etc.). For your case I think #3 is pretty suitable but if you expect to have more than 10 different user roles - try the "hid" approach.
Are you using any kind of framework here? Could these values be stored in a single source - a config file - which both creates a list of the objects in PHP and also populates the table when you bootstrap the database? I'm thinking from a Rails perspective, as it's been a while since I've written any PHP. Solution there would probably be fixtures.
Why not to make it just
foreach (getdbarr("SELECT * FROM usertype") as $row) {
define($row['name'],$row['id']);
}
You shouldn't need a JOIN in every query to fetch the information about types/roles. You can keep your 'user' model and 'role' models separate in the data access objects (DAO) -- especially since there are so few records for user types.
In most cases where I have a limited number of options that I'd otherwise be joining against a large table, I cache them in memcached as an associative array. In the event I need some information about a particular relationship (like a role) I just lazy load it.
$user = DAO_User::get(1); // this pulls a JOIN-less record
$role = $user->getRole(); // lazy-load
The code for $user->getRole() can be something like:
public function getRole() {
// This comes from a cache that may be called multiple
// times per request with no penalty (i.e. store in a registry)
$roles = DAO_UserRoles::getAll();
if(isset($roles[$this->role_id]))
return $roles[$this->role_id];
return null; // or: new Model_UserRole();
}
This also works if you want to display a list with 1000 users on it. You can simply render values for that column from a single $roles associative array.
This is a major performance improvement on the SQL end, and it goes a long way to reducing complexity in your code base. If you have several other foreign keys on the user table you can still use this approach to grab the necessary information when you need it. It also means you can have dependable Model_* classes without having to create hybrids for every possible combination of tables you might JOIN -- which is much better than simply getting a result set, iterating it, and freeing it.
Even with more than 100 rows on both sides of your JOIN, you can still use the lazy load approach for infrequent or highly redundant information. With a reasonable caching service in your code, there's no penalty for calling DAO_UserRole::get(1500) multiple times because subsequent calls during the same request shouldn't hit the database twice. In most cases you're only going to be displaying 10-25 rows per page out of 1000s, and lazy loading will save your database engine from having to JOIN all the extraneous rows before you actually need them.
The main reason to do a JOIN is if your WHERE logic requires it, or if you need to ORDER BY data from a foreign key. Treating JOINs as prohibitively expensive is a good habit to be in.
For basicly static lookup tables, I generally make static constant files (such as your #3). I generally use classes such as:
namespace Constants;
class UserTypes {
const ADMIN = 1;
const USER = 2;
const GUEST = 3;
}
$id = Constants\UserTypes::ADMIN;
When I'm using lookup takes that are a bit more variable, then I'll pull it into a object and then cache it for 24 hours. That way it only gets updated once a day. That will save you from making database round trips, but allow you to deal with things in code easily.
Yeah, you're right about avoiding #3 and sticking with #2. As much as possible, look-ups like when you use a usertype table to contain the roles and then relate them to the user table using the id values should stay in the database. If you use constants, then the data must always rely on your php code to be interpreted. Also, you can enforce data integrity by using foreign keys (where servers allow) and it will allow you to port the reporting from your php code to other reporting tools. Maintenance also becomes easier. Database administrators won't need to know php in order to derive the meanings of the numbers if you used #3, should they ever be asked to aid in reports development. It may not seem too relevant, but in terms of maintenance, using stored procedures than embedded sql in your php code would also be maintenance-friendly in several ways, and will also be advantageous to DBAs.
I'd go for option #2 and use the join as it is intended to be used. You never know what the future will throw up, it's always better to be prepared today!
With regards to leaving the database alone as much as possible for such operations, there is also the possibility of caching in the long term. For this route, within PHP an option is to use a file cache, one that will only get updated when time calls for it. For the framework I have created, here's an example; I'd be interested to know what people think:
Note:
(LStore, LFetch, GetFileName) belong to a Cache object which gets called statically.
(Blobify and Unblobify) belong to a SystemComponent object which is always alive
Each piece of cache data has a key. this is the only thing you ever have to remember
public function LStore($key,$data, $blnBlobify=true) {
/* Opening the file in read/write mode */
$h = fopen(self::GetFileName($key, 'longstore'),'a+');
if (!$h) throw new Exception('Could not write to cache');
flock($h,LOCK_EX); // exclusive lock, will get released when the file is closed
fseek($h,0); // go to the start of the file
/* truncate the file */
ftruncate($h,0);
if($blnBlobify==true) { $data = SystemComponent::Blobify(array($data)); }
If (fwrite($h,$data)===false) {
throw new Exception('Could not write to cache');
}
fclose($h);
}
public function LFetch($key) {
$filename = self::GetFileName($key, 'longstore');
if (!file_exists($filename)){ return false;}
$h = fopen($filename,'r');
if (!$h){ return false;}
/* Getting a shared lock */
flock($h,LOCK_SH);
$data = file_get_contents($filename);
fclose($h);
$data = SystemComponent::Unblobify($data);
if (!$data) {
/* If unserializing somehow didn't work out, we'll delete the file */
unlink($filename);
return false;
}
return $data;
}
/* This function is necessary as the framework scales different directories */
private function GetFileName($key, $strCacheDirectory='') {
if(!empty($strCacheDirectory)){
return SystemComponent::GetCacheAdd() . $strCacheDirectory.'/' . md5($key);
} else {
return SystemComponent::GetCacheAdd() . md5($key);
}
}
public function Blobify($Source){
if(is_array($Source)) { $Source = serialize($Source); }
$strSerialized = base64_encode($Source);
return $strSerialized;
}
public function Unblobify($strSerialized){
$Decoded = base64_decode($strSerialized);
if(self::CheckSerialized($Decoded)) { $Decoded = unserialize($Decoded); }
return $Decoded;
}
function CheckSerialized($Source){
$Data = #unserialize($Source);
if ($Source === 'b:0;' || $Data !== false) {
return true;
} else {
return false;
}
}
Now when it comes to accessing the actual data, I just call a fetch. For making sure it is up to date, I tell it to store. In your case, this would be after updating the usertype table.