For your rudimentary example of object-relational mapping below, yes,
performance is going to be atrocious. That's because you're not taking any
advantage of the features that using OOP gives you. One could write a
dissertation on this problem, but I will just give you some general
guidelines. (All code examples below assume PHP 5.)
1) OOP has overhead. In most situations, if you can write something in a
fully OOP fashion or a direct procedural fashion, the direct procedural will
perform faster. How much faster depends on the situation and the experience
level of the programmer. What OOP can get you is flexibility. There are
cases where OOP code is faster than the corresponding procedural, but none of
them involve SQL. :-)
2) Burn some memory on private variables. That's what they're there for. Eg:
public class Foo {
private $id=0;
private $bar=0;
private $baz='';
function __construct($id) {
// Do your type checking and escaping here
$result = mysql_query("SELECT * FROM Foo WHERE id={$id}");
$record = mysql_fetch_object($result);
$this->id = record->id;
$this->bar = record->bar;
$this->baz = record->baz;
}
function getBaz() {
return $this->baz;
}
}
You'll want to be a lot smarter than the code above to avoid drudge work in
the code, but you get the idea. Front-load as much of the object's
information has you can in the constructor, within reason. A good rule of
thumb is that anything you can get in the initial query (like above) you
should and then cache. That saves you putting wasteful SQL queries in your
getters.
3) Data you have to load later from another table, delay until you need to get
it but then cache that as well.
function getRevenue ($id,$department,$month,$year) {
if (!$this->revenue) {
$result = mysql_query ("SELECT revenue FROM customer_revenue WHERE
customer_id = '$id' AND
department = '$department' AND month = '$month' AND year = '$year'");
$this->revenue = mysql_result ($result, 0);
}
return $this->revenue;
}
Again, pull extra data if you can. A somewhat larger result set is (usually)
better than multiple queries. How much you want to front-load in your object
and how much you want to defer to later depends on your specific problem and
how frequently the data will be used.
4) You can optimize your SQL structure for easier object usage. Often that
just means proper normalization, sometimes you'll want to denormalize in
specific places to improve performance. Again, depends on your situation.
5) If you need to grab 100 objects at once, but just need basic data out of
them, use a factory. Vis,
$myobjects = Foo::getObjects(array(1, 2, 3));
class Foo {
static function getObjects($ids) {
$return = array();
$result = mysql_query("SELECT * FROM Foo WHERE id IN (" . implode(',',
$ids) . ")");
while ($record = mysql_fetch_object($result)) {
$foo = new Foo();
$foo->setProperties($record);
$return[] = clone($foo);
}
return $return;
}
function setProperties($properties) {
// Left as an exercise to the reader
}
}
6) If you need to do a complex query with a couple of joins and such, then
don't waste your time or the computer's trying to shoe-horn it into SQL. SQL
is not inherently OO to start with! Just write your query and loop it and be
happy. OOP is not the answer to all problems. Sometimes it does just make
matters worse, no matter what Sun tries to tell you. :-)
> I want my data to _only_ be accessed from the black box called an OOP
> class.
That will work and is achievable in about 30% of all situations. For the
other 70%, you will have to just hunker down and *gasp* write SQL specific to
the task at hand at least some of the time. How much of the time once again
depends on your situation and the problem you're trying to solve.
On Tuesday 10 October 2006 18:14, Chris de Vidal wrote:
> I think perhaps I'm using classes and OOP incorrectly. The last time I
> used them, they were slow.
>
> I want to create a "customer" class which fetches its attributes from a
> MySQL database. Something like this pseudocode:
>
> class customer
> {
> ...
> getName ($id)
> {
> $result = mysql_query ("SELECT name FROM customers WHERE id =
> '$id'"); return mysql_result ($result, 0);
> }
> getRevenue ($id,$department,$month,$year)
> {
> $result = mysql_query ("SELECT revenue FROM customer_revenue WHERE
> customer_id = '$id' AND department = '$department' AND month = '$month' AND
> year = '$year'"); return mysql_result ($result, 0);
> }
> ...
> }
>
> (Again, that's just psedocode. Haven't written anything yet.)
>
>
> That works great for just one revenue result, but what if I want to return
> several results? What if I want to build a report with hundreds of
> customers' revenues for a month? For several months? For an entire year?
> Doesn't it slow wayyyy down? It seemed to in the past. The method above
> doesn't seem to scale well.
>
> I did something like the above a few years ago. It was slow, and I think
> it's because it was doing this for each single row:
> * Instantiate the class
> * Perform a query for the name
> * Perform a query for the first department
> * Perform a query for the next department
> * Perform a query for the next department
> * Perform a query for the next department
> * Perform a query for the next department
> * Perform a query for the next department
> * Perform a query for the next department
> ...
> * Destroy the instantiation
> * Start over
>
> I love using object-oriented programming, but it seems to me that's ALOT of
> performance burden when I could just do something like this:
> SELECT customers.name,
> customer_revenue.revenue
> FROM customers
> INNER JOIN customer_revenue
> ON customers.id = customer_revenue.customer_id
> WHERE customer_revenue.department = '$department'
> AND customer_revenue.month = '$month'
> AND customer_revenue.year = '$year'
>
> (PHP loop to display all results)
>
>
> That seems like it would perform 20x faster.
>
> It seems that the SQL economies of scale (ability to rapidly slurp large
> sets of data) are completely bypassed when using OOP to view ALOT of
> objects together on one screen. It seems if I want decent performance I
> would have to ignore OOP rules of method hiding and access the data
> directly when using anything more than just a few objects on one page.
>
>
> A workaround -- I thought that perhaps I could fetch ALL of the data for a
> customer into memory upon initial instantiation, wherein I perform
> something like this: SELECT *
> FROM customers
> INNER JOIN customer_revenue
> ON customers.id = customer_revenue.customer_id
> WHERE customers.id = '$id'
>
> And then use the results from memory as-needed. It would be all data for a
> customer from all years across all departments.
>
> That might perform better but it'd suck ALOT of data into memory, and what
> if I add more than just revenue to my customer class? Each customer could
> potentially represent a limitless set of data. So this doesn't seem like an
> optimal solution.
>
>
> * Where am I going wrong?
> * Tell me how YOU fetch data from multiple objects to generate reports.
> * Is this an instance where it's better to just ignore OOP rules and go
> straight to the data? Whew, that's not fun to think about, particularly if
> my queries ever get to be more complex.
>
> Feedback, please. The goal is to maintain complex queries in just one
> place -- inside a class. I want my data to _only_ be accessed from the
> black box called an OOP class.
>
> CD
>
> Think you're a good person? Yeah, right! ;-)
> Prove it and get ten thousand dollars:
> TenThousandDollarOffer.com
--
Larry Garfield AIM: LOLG42
[EMAIL PROTECTED] ICQ: 6817012
"If nature has made any one thing less susceptible than all others of
exclusive property, it is the action of the thinking power called an idea,
which an individual may exclusively possess as long as he keeps it to
himself; but the moment it is divulged, it forces itself into the possession
of every one, and the receiver cannot dispossess himself of it." -- Thomas
Jefferson
--
PHP General Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php