PDA

View Full Version : Datamanagers: Objects as Data and Methods


Danny.VBT
25 Mar 2008, 04:42
If you haven't read this article (http://www.vbulletin.org/forum/showthread.php?t=119376) on how to create custom data managers or perhaps you read it and don't see what the big deal is - please read further!

Why go through the complexity of a data manager when I can just manage my data with direct queries and procedural code?

vBulletin's data managing classes can look pretty complicated if you are unfamiliar with OO programming - but the benefits far surpass the few hurtles of adopting a more OO approach to your programming.

A Live Example

We'll start off with a project simulation. A client wants an application that keeps tracks of restaurant reviews. In order to achieve this, we need to set up a database tables that would look something like this:Table: restaurant

| restaurant id | title | location | menuid | totalreviews

Table: review

| review id | restaurant id | rating | comments | userid

As you can, each review has a rating and user comments and links to a particular restaurant in the database.

A Procedural Approach

Tackling the project is a pretty standard task - the most obvious coding tasks being the insertion, updating, and deleting of restaurant reviews.

(Of course, there could be other aspects too this type of project, but the overall focus will be narrow only to supplement the article's message.)


Here is sample code for restaurant review insertion:



# Clean variables
$vbulletin->input->clean_array_gpc('p', array('comments' => TYPE_STR,
'rating' => TYPE_UINT,
'restaurantid' => TYPE_UINT)
);

# Does the restaurant exist?
$restaurant = $vbulletin->db->query_first("SELECT title FROM " . TABLE_PREFIX . "restaurant
WHERE restaurant_id = " . $vbulletin->GPC['restaurantid']);

if(!$restaurant) {
// Submit error
}

# Is the rating valid? (1-5)
if($rating < 1 OR $rating > 5) {
// Submit Error
}

# Error checking is done, we can add it to the database
$vbulletin->db->query_write("INSERT INTO " . TABLE_PREFIX . "review (userid, restaurant_id, rating, comments)
VALUES (
" . intval($vbulletin->userinfo['userid'] . ", "
. $vbulletin->GPC['restaurantid'] . ", "
. $vbulletin->GPC['rating'] . ", "
. $vbulletin->db->escape_string($vbulletin->GPC['comments']) . ")"
);
// Print Redirect "You're review of $restaurant['title'] was submitted."We did basic error checking and then submitted it into the database. Now let's see some sample code for updating a restaurant review:


#Clean variables
$vbulletin->input->clean_array_gpc('p', array('comments' => TYPE_STR,
'rating' => TYPE_UINT,
'restaurantid' => TYPE_UINT,
'reviewid' => TYPE_UINT
));

#Does the review exist?
$review = $vbulletin->db->query_first("SELECT id FROM " . TABLE_PREFIX ."review
WHERE reviewid = " . $vbulletin->GPC['reviewid']);

if(!$review) {
// submit error
}

#Is the new rating correct?
if($rating < 1 OR $rating > 5) {
// Submit errror
}

#Update database
$vbulletin->db->query_write("UPDATE " . TABLE_PREFIX ."review
SET comments = '" . $vbulletin->db->escape_string($vbulletin->GPC['comments'])) . "'
rating = " . $vbulletin->GPC['rating'] . "
WHERE reviewid = " . $vbulletin->GPC['reviewid']);
The code between updating and inserting looks familiar, doesn't it? Your first thought should be that it looks a little too familiar. One of the quickest signs that your application is going down hill is seeing repeated logic in your code.

But what does this have too do with OO programming and objects? Can't we just code functions to prevent this code duplication?

The OO Mentality: Objects as Data and Methods

With procedural programming, data and behavior are two completely separate entities. You input data into a function and get specific data back. This may not be a problem at times but the data manager classes bring up a specific benefits when handling data.

Since objects are both data and methods, you can avoid messy situations with verifying data. The vBulletin Data Managers offer a nice clear and encapsulated way to check inputted variables. The class contains an array of valid fields which specify methods to verify the inputted data.

Our restaurant review would look something like this:


var $validfields = array('reviewid' => array(TYPE_UINT, REQ_INCR, VF_METHOD, 'verify_nonzero'),
'restaurantid' => array(TYPE_UINT, REQ_YES, VF_METHOD, 'verify_restaurant'),
'comments' => array(TYPE_NOHTML, REQ_YES),
'rating' => array(TYPE_UINT, REQ_YES, VF_METHOD, 'verify_rating')
);
See how the validfields key VF_METHOD maps to a specific method within the class that only performs one piece of logic. This not only offers a clear view on what method is handling what data, but it also encapsulates logic such that if you ever wanted to extend this data manager in the event of a future version with more extensive features or a simple change in feature, you can do it very easily.

Here would be some of the basic verification methods, all short and sweet:


function verify_restaurant($restaurantid) {
$restaurant = $this->registry->db->query_read("SELECT title FROM " . TABLE_PREFIX ."restaurant WHERE restaurantid = " . $restaurantid);
if(!$restaurant) {
return false;
}

return true;
}

function verify_rating($rating) {
return ($rating < 1 OR $rating > 5) ? false : true;
}With the data manager object, you neatly encapsulate the data and methods into a single entity. With a procedural approach, you'd have to pass the data quite messily along each function - or even worse, couple separate logic into a single function.

Also, with the object - you get increased portability. Adopting to the original insertion and update code with the data manager.


$reviewdm =& datamanager_init('Review', &$vbulletin, $errtype = ERRTYPE_STANDARD, $forcefile = 'review');
$reviewdm->set('restaurantid', $vbulletin->GPC['restaurantid']);
$reviewdm->set('comments', $vbulletin->GPC['comments']);
$reviewdm->set('rating', $vbulletin->GPC['rating']);
$reviewdm->pre_save();
if (count($reviewdm->errors) > 0)
{
$errors = "<ul>";
foreach($reviewdm->errors As $err)
{
$errors .= '<li>' . $err . '</li>';
}
$errors .= '</ul>';
eval(standard_error($errors));
}
else
{
$reviewdm = $reviewdm->save();
}
Benefits:


It's not just your datamanager class data and methods you have at hand, but the base datamanager class you are extending from. This class hierarchy and code reuse you can not achieve with just procedural functions alone. You can simply alter some basic settings (the valid fields array, the table array, etc...etc...) and leave the bulk of the processing to the default class.
Easily portable: You can call the datamanager class from anywhere, the forums, the admin cp, and even any custom scripts you create powered by the vBulletin engine. (Calling global.php)
Keeps separate operations in there own methods and reduces code duplication.If you have to make an alteration to the business logic (say you want to restrict review comments to 500 characters) you only have to add a verify_comments() method to your class and alter the valid fields array.
Inheritance allows for easy extension of feature and entire sub-systems. If your client now wants the review system to accept not only restaurants but clubs and casinos, you can accomplish it easily by just extending from the default Review data manager class.Conclusion:

The intention of this article was to shed to light the benefits of OO programming through a tool utilized and available to you in vBulletin. This article is focused on custom data managers, but the sky is the limit for creating OO applications. Good luck, and don't be afraid to take the dive into the powerful paradigm of object oriented programming.

Additional Reading:


Creating Custom Datamanagers (http://www.vbulletin.org/forum/showthread.php?t=119376)
Managing Coder Updates Effectively (http://www.vbulletin.org/forum/showthread.php?t=119376)

Come2Daddy
04 Sep 2009, 20:34
Thanks for sharing actually I'm not professional OOP, but I know about it as a philosophy I studied it & passed it in college but I don't have the skills to program in OOP style

so I'm not sure of how much have understood of this article, but I wonder why didn't you make the restaurantid auto increment

besides when we check for existence how could we know the restaurantid if we have thousands of restaurants

finally is it a must to use datamanger in inserting data into database via a form embedded in a powered by vbulletin page??? or we can just do procedurally ?

Antivirus
06 Sep 2009, 17:40
is it a must to use datamanger in inserting data into database via a form embedded in a powered by vbulletin page??? or we can just do procedurally ?

You can do it procedurally if you feel more comfortable doing it that way. It's really up to you as the coder. If a datamanager already exists however (for instance, in the case of users, posts, forums, etc...) then i would suggest ALWAYS using the datamanager supplied by vbulletin. The reason I suggest this, is because often the datamanager class will update other tables which the record in the primary table interacts with.

For instance: Say you want to write a query to create a reply by a specific user to a certain thread automatically. This could easily be done with a query, however you must also remember to do a slew of other things such as update the post count of the user making the reply, update the user's last reply dateline field, etc... The datamanager always handles
these things behind the scenes which you don't have to worry about - otherwise if you don't do all these things your forums can easily get out of whack. When creating a datamanager for yourself, the key is to code these issues into it to avoide complications later on.

Come2Daddy
09 Sep 2009, 08:43
Thanks Antivirus

your answer added a lot to knowledge, now the picture is clearer
thanks a gain