Jump to content

Class Design- objects/classes inside classes- Critique my idea


JoeBuntu

Recommended Posts

I'm not new to programming, but I am trying to get a handle on OOP.

 

I have an idea on a class I want to create for an HTML table, and I'd like someone to critique my plan before I start going down the wrong path with this OOP thing. I'm not sure if what I am trying to do with using classes within classes is good OOP design.

 

The short and sweet idea is to have a table class with row classes, with the row classes containing column classes. And I want to say that I'm sure I'm reinventing the wheel, there probably is a much better table class out there than what I will ever come up with- that's ok, I'm trying to learn.

 

Idea:

I will start with a "Table" class.

Possible Table methods:

    1) get/import data

    2) print header row

    3) print rows

    3) print footer row (maybe)

    4) print buttons (maybe for navigation)

Table Properties

    1) Header row

    2) id, name, css class, events

    2) main row(s)

 

My idea is to make the rows their own classes- Not so much for re-usability, but more so for encapsulation and what I think will be better organization than an associative raw.

 

Possible row class:

    methods

      1) add column

      2) change different properties

    properties

      1) events

      2) id, name, css class

 

Then also their would be a column class:

      propeties

        1) type of column ( plain text, label, textbox, button, radio, etc)

              type will determine what other properties- So I'm thinking of creating a Parent class and extending that class for the different types of colums

 

Thanks for your input!

       

 

 

 

 

 

Link to comment
Share on other sites

I think creating classes for HTML tables is kind of a waste of time unless you have a specific one that you need to use a lot but not for general purpose.

 

It would take just as much effort, even less, to use regular HTML table tags, rather than create all these objects and call all these methods.

 

Maybe I'm not thinking dynamically enough but I can't see an advantage in creating classes for each row...

 

Creating HTML template for certain pages is a good idea, but that's entirely different than OO tables.

 

Maybe someone else can give you more positive feedback on this  ;D

Link to comment
Share on other sites

yes -positive feed back would be nice!

like I said this is for learning purposes. I do have a project I plan on using it with though.

I wouldn't actually be creating a class for every row.

I would create an instance for the header row if needed, one for the main section of rows and call a print loop, and maybe a footer row or something.

Link to comment
Share on other sites

It sounds like a very .NET approach to PHP.  The biggest hurdle is with data binding.  How will you handle creating the table and storing values inside it?  Doing something like:

 

$table = new Table("ID", "Name");
$table->addValues(1, "Bubba");

 

Is a lot more palatable than doing something like:

 

$table = new Table();
$headerRow = new Row();
$idColumn = new PlainTextColumn("ID");
$nameColumn = new PlainTextColumn("Name");

$headerRow->addColumn($idColumn);
$headerRow->addColumn($nameColumn);

$table->addRow($headerRow);

 

And that's before any data is even added to the table.

Link to comment
Share on other sites

Thats funny you say that- I started out with VB.NET.

By data binding I think you mean setting properties and invoking methods of the objects I create on the fly- I think I have that figured out.

 

Table creation would look like this:

//array will initialize table class column names
$fieldNames = array('ClientAccount', 'lastName', 'FirstName');

$table = New Table($fieldsNames);

$table->rows->addRow('Header');
//on creation of rows, column creation will be initialized by using //$feildNames to determine a naming scheme and the number of columns- not that columns could not be added
$table->rows->Header->SetSomeProperty($JavaEvent);

$table->rows->addRow('Body');
$table->rows->Body->SetSomeProperty($cssClass);
$table->rows->Body->Columns->ClientAccount->SetSomeProperty($JavaEvent);

//Do other stuff- pull data from MySql to populate table
// Table would loop through query result, extracting data, 
//This wouldn't be the only method for populating table.

$table->Display();

 

I'm still looking for someone to tell me if this is good or bad OOP design.

 

This is what I was aiming for with my Table class:

 

1) to be flexible

    flexibility- 1 or 100 columns

                  plain text, textbox, events

                 

2) Easy to use interface

 

If this is a bad plan please by all means steer me away. I know the implementation might be tricky, but who cares as long as the interface is not tricky.

 

 

 

Link to comment
Share on other sites

I understand where you are coming from. But wouldn't this make designing the layout extremely difficult?

 

My websites, as well as others have nested HTML tags within HTML tags, with javascript all over the place...

 

At least by using something like Dreamweaver I can visually create the layout and put PHP in "pockets" of areas where it needs to be.

 

I have always been curious myself what exactly is the right design if there even is any?

 

I thought that proper framework design was to take the logic out of the design and keep them separated......?

Link to comment
Share on other sites

Thats funny you say that- I started out with VB.NET.

By data binding I think you mean setting properties and invoking methods of the objects I create on the fly- I think I have that figured out.

 

Table creation would look like this:

//array will initialize table class column names
$fieldNames = array('ClientAccount', 'lastName', 'FirstName');

$table = New Table($fieldsNames);

$table->rows->addRow('Header');
//on creation of rows, column creation will be initialized by using //$feildNames to determine a naming scheme and the number of columns- not that columns could not be added
$table->rows->Header->SetSomeProperty($JavaEvent);

$table->rows->addRow('Body');
$table->rows->Body->SetSomeProperty($cssClass);
$table->rows->Body->Columns->ClientAccount->SetSomeProperty($JavaEvent);

//Do other stuff- pull data from MySql to populate table
// Table would loop through query result, extracting data, 
//This wouldn't be the only method for populating table.

$table->Display();

 

I'm still looking for someone to tell me if this is good or bad OOP design.

 

This is what I was aiming for with my Table class:

 

1) to be flexible

    flexibility- 1 or 100 columns

                  plain text, textbox, events

                 

2) Easy to use interface

 

If this is a bad plan please by all means steer me away. I know the implementation might be tricky, but who cares as long as the interface is not tricky.

 

Heh, I'm currently doing an ASP.NET/C# project myself.

 

With PHP, one of the things you need to keep in mind is that it doesn't have the property mechanism of VB.NET/C#.  So, I'm a bit concerned seeing code like:

$table->rows->addRow('body');

 

PHP doesn't check against type, so there's an inherent risk of losing encapsulation along the way.  If I'm reading your example correctly, 'rows' is a public field of $table - most likely a Row object or an array of Row objects.  This field can be overwritten by anything.  Even something like:

$table->rows = 1234;

 

And PHP won't give an error.

 

If you're already aware of this, then I suggest getting rid of the 'rows' portion altogether and having an interface like:

$table->addRow('body');

 

Instead.

Link to comment
Share on other sites

 

 

PHP doesn't check against type, so there's an inherent risk of losing encapsulation along the way.  If I'm reading your example correctly, 'rows' is a public field of $table - most likely a Row object or an array of Row objects.  This field can be overwritten by anything.  Even something like:

$table->rows = 1234;

 

And PHP won't give an error.

 

If you're already aware of this, then I suggest getting rid of the 'rows' portion altogether and having an interface like:

$table->addRow('body');

 

Instead.

 

But wait, PHP has some form of type hinting....

 

 

Link to comment
Share on other sites

I understand where you are coming from. But wouldn't this make designing the layout extremely difficult?

 

My websites, as well as others have nested HTML tags within HTML tags, with javascript all over the place...

 

At least by using something like Dreamweaver I can visually create the layout and put PHP in "pockets" of areas where it needs to be.

 

I have always been curious myself what exactly is the right design if there even is any?

 

I thought that proper framework design was to take the logic out of the design and keep them separated......?

 

Nested tables could be a problem, but nested HTML tags, in general, could be solved easily.  Just save cell data as a string, tags included.

 

Regarding JavaScript - this is where unobtrusive JavaScript comes in.  In most cases, if you're using inline JavaScript calls, you're doing it wrong.  There's no reason why JavaScript should be embedded in HTML when there are a variety of ways to get element references from outside the markup.

 

What the OP is trying to do is limit the size and scope those PHP 'pockets'/islands have on a page template.  Instead of having a big where-loop in the middle of a template, he can simply write:

<?php $table->print(); ?>

 

And the entire thing will be output properly.  So, in short, the OP is trying to strengthen the divide between logic and output with his OO tables.

 

It's unfortunate that PHP doesn't adopt a full code-behind model to compliment its inline model.  It'd save some design headaches in the long run.

Link to comment
Share on other sites

 

 

PHP doesn't check against type, so there's an inherent risk of losing encapsulation along the way.  If I'm reading your example correctly, 'rows' is a public field of $table - most likely a Row object or an array of Row objects.  This field can be overwritten by anything.  Even something like:

$table->rows = 1234;

 

And PHP won't give an error.

 

If you're already aware of this, then I suggest getting rid of the 'rows' portion altogether and having an interface like:

$table->addRow('body');

 

Instead.

 

But wait, PHP has some form of type hinting....

 

As far as I can tell, PHP's type hinting isn't available for object members/fields.  Instead, it only works with method arguments.

Link to comment
Share on other sites

Thanks for the input everyone- I have a few things to consider now.

  Nightslyr- as far as that javascript. The only thing I had planned on doing was providing some methods to add events to the  different elements- such as

$table->$rows->$headrow->addEvent("onClick="javafunction")

Are you saying this would be a bad idea? I have hardly used javascript so I'm not that familiar with other ways to dynamically attach it as I would like to. Please Elaborate

 

I'll have to think about the addrows functionality. The reason I was going for a $table->$rows object was to be able to treat the rows as a collection...I'll have to think about it.

 

since no one flat out told me this is a crappy idea I'm going through with it- I'll post what I come up with this weekend.

 

Link to comment
Share on other sites

Thanks for the input everyone- I have a few things to consider now.

   Nightslyr- as far as that javascript. The only thing I had planned on doing was providing some methods to add events to the  different elements- such as

$table->$rows->$headrow->addEvent("onClick="javafunction")

Are you saying this would be a bad idea? I have hardly used javascript so I'm not that familiar with other ways to dynamically attach it as I would like to. Please Elaborate

 

I'll have to think about the addrows functionality. The reason I was going for a $table->$rows object was to be able to treat the rows as a collection...I'll have to think about it.

 

since no one flat out told me this is a crappy idea I'm going through with it- I'll post what I come up with this weekend.

 

 

Hey dude, I'd be more than happy to "hang" with you on this project, I think you are a bit more ahead of me in OOP but I am looking to design in OOP myself. Feel free to PM me, and I can check out your website as you work on it and perhaps we can learn this thing together, and bounce back ideas....

 

 

Link to comment
Share on other sites

I wanted to post what I have come up with over the weekend- Feel free to tell me what you think about my very first class.

 

I still have work to do, but it works to some degree and you can get a feel for how the development will continue just by looking at what I have so far.

 

I decided to ditch the table class because it was becoming to complex for me- especially since this is the first practical class I have written in any language. My head was starting to spin in circles.

I decided to come up with row class instead- the next best thing which will handle a bulk of what the table class would handle anyway.

 

I posted the classes below as well as a simple implementation, but let me give a brief explanation of how the class would be used.

 

The row class takes two parameters:

1) an array containing column names and labels of fields that will be displayed.

2) a mysql query result- this will contain all data one might wish to output to a page. The 1st parameter determines which of fields will be displayed, the rest of the data will be available to print in bulk as hidden input types.

 

Once the class is initiated a header row is internally generated and it's properties may be adjusted with the row classes methods before the printHeader method is called.

 

Also the properties for the visible columns may be changed before calling the printRows() method.

 

When the printRows method is called, output is also created for the hidden columns for later output. Now that I type this though, I should probably make the two independent of each other.

 

 

Implementation of the class:

 


<?php

require_once("newRow.inc");


$query = 'SELECT * FROM ClientProfile';
$db = new mysqli('localhost', '****', '*****', '****');

if (mysqli_connect_errno()){
	echo "Error: Could not connect to database. Please try again later.";
	exit;
}


$result = $db->query($query);

// array( array('TableColumnName', 'Label'))
$fields = array( array('ClientAccount', 'Account'));
$bob = new Row($fields, $result);
echo '<html><body><table>';
$bob->printHeader();
$bob->printRows();
echo '</table>';
print $bob->printHiddenContent();
echo '</body></html>';
echo 'john';
$db->close();
?>

 

and the row class along with the column classes it implements

 

<?php

class Row
{
protected $_displayFields = array();
protected $_headerContents;
protected $_headerAttributes = array();
protected $_data;
protected $_attributes = array();
protected $_contents;
protected $_hidden_content;

 public function __construct($displayFields, $mysqliResult)
 {
 	$this->_data = $mysqliResult;
 	$this->_initDisplayFields($displayFields);
 	$this->_generateHeaderContent();
		$this->_extractHiddenCols();

 }

 public function setHeaderClass($class)
 {
 	$this->_setHeaderAttribute('class', $class);
 }

 public function setHeaderId($id)
 {
 	$this->_setHeaderAttribute('id', $id);
 }

 public function setHeaderEvent($event)
 {
 	$this->_setHeaderAttribute('event', $event);
 }	 	  
 public function printHeader()
 {
 	print '<tr ' . implode(' ', $this->_headerAttributes) . '>'
 	      . $this->_headerContents . '</tr>';
 }

 protected function _setHeaderAttribute($attribute, $value)
 {
 	try{
 		if(!is_string($value))
 		throw new Exception('header ' . $attribute . ' must be a string', 1005);

 		if($attribute = 'event')
 		{
 				$this->$_headerAttributes['event'] = $value;

 		} else{
 				$this->_headerAttributes[$attribute] = $attribute . '="' . $value . '"';
 		}

 	}

    catch(Exception $error)
      	{
      		print _formatException($error);
      	}

 }	 
 protected function _generateHeaderContent()
 {
 	$content = '';
 	//generates a header row using labels of visible rows
 	foreach($this->_displayFields as $current)
 	{
 		$content = $content . '<td>' . $current->getLabel() . '</td>';
 	}

 	$this->_headerContents = $content;
 	$this->_headerAttributes['class'] = 'class="headrow"';
 	$this->_headerAttributes['id'] = 'id="headrow"';

 }




    protected function _initDisplayFields($fields)
    {
    
    try{
      	
      	if(!is_array($fields))
      		throw new Exception("display fields array not an array", 1001);
      		
      	foreach($fields as $current)
      	{
      		$name = $current['0'];
      		$label = $current['1'];
      		
      		if(!is_string($name))
      			throw new Exception("display field name not a string", 1002);
      		if(!is_string($label))
      			throw new Exception("display field label not a string", 1003);
      		$column = new VisibleColumn($name, $label);
      		     		
      		$this->_displayFields[$name] = $column;
      		     			
      	}
      
      	} 
      	catch(Exception $error)
      	{
      		print _formatException($error);
      	}
      	 
    }
    protected function _extractHiddenCols()
    {  	
     //creates hidden column for query columns not included
     //in display array
     try{

		//if(!is_object($mysqliResult))
		//throw new Exception('Query data not object', 1006);

	mysqli_data_seek ($this->_data,0 );

		if(mysqli_num_rows($this->_data) == 0)
		throw new Exception('MySQL reult contains zero rows', 1007);
	$row = mysqli_fetch_assoc($this->_data);

	foreach($row as $key=>$current)
	{
		if(!array_key_exists($key, $this->_displayFields))
		{
			$column = new HiddenColumn($key);
			$this->_displayFields[$key] = $column;
		}

	}

 	}
 	catch(Exception $error)
      {
      	print $this->_formatException($error);
      }
    }
    
    protected function _formatException(Exception $e)
    {
    	return "Error {$e->getCode()}: {$e->getMessage()}
    				(line: {$e->getline()} of {$e->getFile})";
    }

    public function printRows()
    {
    	//cycles through query data, passes data to column objects and
    	// invokes their print methods,
    	mysqli_data_seek ($this->_data,0 );
    	
    	while($row = $this->_data->fetch_Object())
    	{
    		$buffer = '';
    		foreach($row as $key=>$value)
    		
    		{    			
    			$this->_displayFields[$key]->setData($value);
    			if($this->_displayFields[$key]->getType() == 'Hidden')
    			{
    				$this->_hidden_content = $this->_hidden_content . 
    					$this->_displayFields[$key]->printContent();
    				continue;
    				echo is_null($this->_hidden_content);
    			}
    			$buffer = $buffer . $this->_displayFields[$key]->printContent();
    				
    		}
    		echo $buffer;
    	}
    	//print_r($this->_displayFields);
    	
    }
    
    public function printHiddenContent()
    {
    	try{
    	if(!$this->_hidden_content)
    		throw new Exception("no hidden content to display", 1008);
    		return $this->_hidden_content;
    	    	
    	}
    	catch(Exception $error)
      	{
      		print $this->_formatException($error);
      	}
    	
    	
    }
    
    
    
}

abstract class column
{
protected $_name;
protected $_type;
protected $_value;
protected $_id;


public function getType()
{
	return $this->_type;
}

public function setData($value)
{
	$this->_value = $value;
}

public function printContent(){}


}


class VisibleColumn extends Column
{
protected $_label;
protected $_col_num;
static protected $_count = 0;
protected $_attributes = array();



public function __construct($name, $label)
{
	$this->_name = $name;
	$this->_type = 'Visible';
	$this->_label = $label;
	$this->_col_num = ++self::$_count;
	$this->_attributes['class'] = 'class = "co' . $this->_col_num . '"';

}

public function getLabel()
{
	return $this->_label;
}

public function printContent()
{
	$output = '<td ';
	$output = $output . implode(' ', $this->_attributes) . '>'
 	      . $this->_value . '</td>';
    return $output;
}

}

class HiddenColumn extends Column
{

public function __construct($name)
{
	$this->_name = $name;
	$this->_type = 'Hidden';

}

public function printContent()
{
	$output = '<input type="hidden" name="' . $this->_name . '" ';
	$output = $output . 'id="' . $this->_id . '" ';
	$output = $output . 'value="' . $this->_value . '"></input>';		
	return $output;
}

 

 

 

 

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.