PHP - Classes: Issues and samples for Class Noobs (like me :)

Greg K's picture

He has: 2,145 posts

Joined: Nov 2003

Ok, here is code I used, and while it seems to work, as I'm new to properly using classes in PHP 5, wanted to check with people who may know better:

<?php
  
class Class1
  
{
      private
$value1 = \"\";
      private
$value2 = \"\";
     
      public function __construct(
$val1=\"\",$val2=\"\")
      {
        
$this->value1 = $val1;
        
$this->value2 = $val2;
      }
     
      public function merge()
      {
         return
$this->value1 . ' ' . $this->value2;
      }
   }
  
   class Class2
   {
      private
$data1 = \"\";
      private
$class1 = null;
     
      public function __construct(
$full='')
      {
         list(
$last,$first) = split(',',$full);
        
$this->class1 = new Class1($first,$last);
      }
     
      public function getName()
      {
         return
$this->class1->merge();
      }
   }
  
  
$test = new Class2('Smith,Joe');
   echo
$test->getName();
?>

While this is a basic example, here is the main reason I am needing this. I have a Database class that is needed for our program (it encrypts and decrypts data saved out). I also have a report class that will get report data for pages people come to. I would liek the report to actually make a connection via the database class. So I need to have a class use another class. (this report class will get called by several "systems" on our site)

Any suggestions on how to better set this up would be appriciated. At first I didn't think the above would work since Zend Studio didn't "auto complete" the line $this->class1->merge(). I gave it a try anyhow, and it seemed to work Smiling

-Greg

pr0gr4mm3r's picture

He has: 1,502 posts

Joined: Sep 2006

That code works. There is no reason that I know of why you couldn't have an object inside of a class. In languages like VB.NET or other .NET languages, you have objects and classes all over the place.

Another way of doing that example is using some simple class inheritance:

<?php
  
class Class1
  
{
      private
$value1 = \"\";
      private
$value2 = \"\";
     
      public function __construct(
$val1=\"\",$val2=\"\")
      {
        
$this->value1 = $val1;
        
$this->value2 = $val2;
      }
     
      public function merge()
      {
         return
$this->value1 . ' ' . $this->value2;
      }
   }
  
   class Class2 extends Class1
   {
      private
$data1 = \"\";
      private
$class1 = null;
     
      public function __construct(
$full='')
      {
         list(
$last,$first) = split(',',$full);
         parent::__construct(
$first,$last);
      }
     
      public function getName()
      {
         return parent::merge();
      }
   }
  
  
$test = new Class2('Smith,Joe');
   echo
$test->getName();
?>

Either way works.

carloncho's picture

They have: 17 posts

Joined: Jan 2008

Its OK, but why you use parent::merge() in getName method of Class2 against $this->merge() ?

Greg K's picture

He has: 2,145 posts

Joined: Nov 2003

I considered doing inheritance, but seemed to not fit the true nature of OOP, where for an inherited class, the one class should be a sub class of another (given he usual OOP example... Class Dog inherits class Mammals, as a dog is a mammal, in my case the report is not an extension of the database, it merely uses it.)

The big problems with OOP for me is that I'm the type that learns from good workable examples, and in college and most books, the OOP examples are really simple and my mind keeps saying "you could do that easier without a class" lol

-Greg

Abhishek Reddy's picture

He has: 3,348 posts

Joined: Jul 2001

Given that the merge method is public, I don't see the benefit of inheritance in that example.

This style of OOP (Java-like, I guess) isn't that nice. And PHP's version of it is even less tolerable. It can be futile in many cases.

Greg K's picture

He has: 2,145 posts

Joined: Nov 2003

Here is another one on classes...

For the data our reports generate, all but one has this issue: all data that is used in the report is in group A or group B, and sometimes we need info from the sum of both. Well I have that handled no problem each "get" function has a parameter $limit which is set to either 'A', 'B' or 'T' (for total), and defaults to 'T'

Also, each "get" function get passed a Constant that tells if it is returning an actual value (to be used in another calculation) or a formatted value for display directly on the page.

I have it defined similar to this:

<?php
private $sales = array('A' => false, 'B' => false, 'T' => false);

public function
getSales($limit,$retType=VALUE)
{
  if (
$sales[$limit]===false)
  {
   
// Value has not yet been defined so set it
   
if ($limit == 'T')
     
$this->sales['T'] = $this->getSales['A'] + $this->getSales['B'];
    else
    {
     
// Make necessary calls to database for the values
      // THIS CODE HERE IS ALL THAT IS REALLY DIFFERENT PER WHAT VALUE
      // WE ARE TRYING TO GET. THE REST OF THE FUNCTION IS THE SAME
      // FOR ALL VALUES OTHER THAN THE ACTUAL VARIABLE NAMES
     
$this->sales[$limit] = $whatever;
    }
  }
  if (
$retType==VALUE}
    return
$this->sales[$limit];
  else
    return
'$' . number_format($this->sales[$limit],2);
}
?>

Here is my logic on doing this way:

1. Using of initial values of FALSE:

Most items being "got" are being used within other "get" functions (ie. getSalesRatio('A') would need the sales figures for group A and the total sales to return a % of how much group A did). So since a value may be used several times, I only want to make one call to the DB. This way once it is set, it merely returns the value based on it's formatting options.

2. The use of RetType

I want to strip as much "coding" as possible off of the web pages, just have two lines, one to include the Report class and one line to initialize it. After that I want to be able to just do (class assigned to $rep and DISP is one of the defines in the class to tell it to display the number formatted):

Group A Sales: <?= $rep->getSales('A',DISP) ?>
instead of
Group A Sales: $<?= number_format($rep->getSales('A',DISP),2) ?>

and yes, after previewing this post, I see I could make it even better by having a separate $rep->getSales('A') and $rep->dispSales('A')

Again, for our reports, there are many of these used and some displayed in more than one place on a report, so simplifying final HTML output is key. (note, some functions return whole #'s, some are currency values, some are percentages, so with this, the HTML coder doesn't have to worry about how to format a number given by the report class, (s)he just tells it that they need it formatted.

Now that I have given that example for others to possible use if they have something similar, here is my question:

How can I simplify this more? As you can see in my code, other than final formatting, the code for each function is about the same except for where it initially needs the data from the database (or calculating from other values). So I would like to make a function like the following:

<?php
 
public function fetch($what, $limit='T', $retType=VALUE)
  { ... }
?>

where I can call it $rep->fetch('sales','A',DISP)

this way all the code to check to see if it is already set, if you are asking for a total and checking the formatting, are in the main fetch function and the code to actually retrieve the values are in a private function.

I am thinking along the lines of this, once you figure out you need fresh values (any error checking would be added as well)

<?php
 
eval('$this->' . $what . '[$limit] = get' . $what . '($limit);');
?>

so that if $what='Sales' it would evaluate into:
$this->Sales[$limit] = getSales($limit);

Any thoughts of another way of doing this, knowing I have about 20 different GET functions needed so using "switch" would not be good due to the messiness of the code.

-Greg

Abhishek Reddy's picture

He has: 3,348 posts

Joined: Jul 2001

You're heading down the right track with wanting to factor out that code.

Firstly, you might be able to eliminate both bits of boilerplate without the fetch function.

Greg wrote: 1. Using of initial values of FALSE:

Is there any reason not to fetch the $sales values in the constructor? That way you do one DB call and that's it. You can add a sync method to update the $sales values, too. So now you won't have to test if the value has been fetched or not, instead you can assume it has been.

Greg wrote: 2. The use of RetType

Just thinking aloud here: maybe you could decouple the display from the business logic by creating another set of classes dedicated to display. If you have a Sales class, then maybe create a DisplaySales subclass which contains all the formatting rules ($retType stuff). Your logical classes then can return only values, and your frontend person will only use the Display classes. This way you can even update the display behaviour without modifying the business logic code.

Secondly, as for the fetch function, it seems like using callbacks would fit right in. Something like:

<?php
public function fetch($what, $limit='T', $retType=VALUE)
{
  return
call_user_func(array($this, 'get' . $what));
}

// the caller:
fetch ('SalesRatio','A',DISP);
?>

That's totally untested, but it should give you an idea. However, if you can lose the boilerplate as I suggested at first then it may not be necessary.

Want to join the discussion? Create an account or log in if you already have one. Joining is fast, free and painless! We’ll even whisk you back here when you’ve finished.