Jump to content

Recommended Posts

It is my hope that this thread can help illustrate the process of migrating one's thinking from a pure procedural point-of-view to one that is more OOP-oriented.  In particular, I hope newbies will read this thread and see, through the successes and failures that will be put on display, what benefits can be gained by using OOP methodology in their projects.

 

I am by no means an OOP master.  In fact, I haven't really tried any of the patterns I've read about.  The aim of this thread is to change that, as I will (with, hopefully, the help of some of the OOP veterans on here) change two working procedural scripts into OOP scripts.  I feel that using real-world scripts that I've written in the past will lend OOP a sense of tangiblity that most examples seem to lack.  Mods, please feel free (like I have any say in the matter ;) ) to move this thread to application design if necessary.  I put it here as I'd like for drive-bye OOP hopefuls to see and even partake in the discussion.

 

Now, onto the show!

 

This project is about changing my registration_viewer and registration_controller scripts from the PHP-Fusion site I just finished into OOP scripts.  I have several reasons for wanting this change:

 

1. I want to learn OOP in practice, so I figured this would be a good place to start.

2. My scripts desperately need help as there are conditionals everywhere.

3. There is no 3.

 

The most logical place to start is at the beginning:

 

My PHP-Fusion project entailed me installing a 3rd party event calendar, then creating a custom registration script for it, allowing users to register for events.  The scripts I show below control the edit/delete process.  registration_viewer polls the database and returns a dropdown menu of registrations that a user has created, with the added caveat that site administrators can view ALL event registrations as well as their own.  Registration_controller pulls up the registration(s) the user chose, and gives them the option to edit (only one) or delete them (multiple).  Administrators, when viewing ALL registrations, can only delete them.

 

The code --

registration_viewer.php

<?php

require_once "maincore.php";
require_once "subheader.php";
require_once "side_left.php";

if(!iMEMBER){
fallback();
}

echo "<span style='font-weight: bold; font-size: 1.25em;'>Edit/Delete Registrations</span><br /><br />\n";

if(iADMIN){ //if ADMIN: two lists, one for the events ADMIN registered to, one for ALL events
$personalQuery = "SELECT * FROM ". DB_PREFIX ."aflac WHERE user_id = '". $userdata['user_id'] ."'";
$personalResult = dbquery($personalQuery);

$adminQuery = "SELECT * FROM ". DB_PREFIX ."aflac";
$adminResult = dbquery($adminQuery);

echo "<form method='post' action='registration_controller.php'>\n<br />\n";
echo "Your registrations: <br /><br />\n";
echo "<select name='personalEvents[]'>\n<br />\n";
echo "<option value='*'>All</option>\n<br />\n";

while($row = mysql_fetch_assoc($personalResult)){
	echo "<option value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}'>{$row['ev_title']} -- ". date('M j, Y', $row['login_timestamp']) ."</option>\n<br />\n";
}

echo "</select>\n";
echo "<input type='submit' name='personalSubmit' value='Go' />\n<br />\n<br /><br /><br /><br />\n";

echo "All registrations: <br /><br />\n";
echo "<select name='adminEvents[]'>\n<br />\n";
echo "<option value='*'>All</option>\n<br />\n";

while($row = mysql_fetch_assoc($adminResult)){
	echo "<option value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}'>{$row['registering_agent']}: {$row['ev_title']} -- ". date('M j, Y', $row['login_timestamp']) ."</option>\n<br />\n";
}

echo "</select>\n";
echo "<input type='submit' name='adminSubmit' value='Go' />\n<br />\n<br />\n</form>\n";
}

if(iMEMBER && !iADMIN){ //if MEMBER && !ADMIN: one list of events MEMBER registered for
$query = "SELECT * FROM ". DB_PREFIX ."aflac WHERE user_id = '". $userdata['user_id'] ."'";
$result = dbquery($query);

echo "<form method='post' action='registration_controller.php'>\n<br />\n";
echo "Your registrations:<br /><br />\n";
echo "<select name='events[]'>\n<br />\n";
echo "<option value='*'>All</option>\n<br />\n";

while($row = mysql_fetch_assoc($result)){
	echo "<option value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}'>{$row['ev_title']} -- ". date('M j, Y', $row['login_timestamp']) ."</option>\n<br />\n";
}

echo "</select>\n";
echo "<input type='submit' name='submit' value='Go' />\n<br />\n</form>\n<br />\n";
}

require_once "side_right.php";
require_once "footer.php";

?>

 

registration_controller.php

<?php

require_once "maincore.php";
require_once "subheader.php";
require_once "side_left.php";

if(!iMEMBER){
fallback();
}

if(isset($_POST['edit'])){ //for editing registrations.  only 1 registration can be edited at a time
if(isset($_POST['events'])){ //MEMBER
	$event = explode(", ", $_POST['events'][0]);

	$user_id = $event[0];
	$ev_id = $event[1];
	$login_timestamp = $event[2];

	$query = "SELECT * FROM ". DB_PREFIX ."aflac WHERE user_id = '$user_id' AND ev_id = '$ev_id' AND login_timestamp = '$login_timestamp'";
	$result = dbquery($query);
	$row = mysql_fetch_assoc($result);

	header("Location: ". BASEDIR ."registration.php?evid={$row['ev_id']}&regAgent={$row['registering_agent']}&agentWritingNum={$row['agent_writing_number']}&phone={$row['phone']}&email={$row['email']}&regSales={$row['regional_sales_coordinator']}&disSales={$row['district_sales_coordinator']}");
}
else if(isset($_POST['personalEvents'])){ //ADMIN
	$event = explode(", ", $_POST['personalEvents'][0]);

	$user_id = $event[0];
	$ev_id = $event[1];
	$login_timestamp = $event[2];

	$query = "SELECT * FROM ". DB_PREFIX ."aflac WHERE user_id = '$user_id' AND ev_id = '$ev_id' AND login_timestamp = '$login_timestamp'";
	$result = dbquery($query);
	$row = mysql_fetch_assoc($result);

	header("Location: ". BASEDIR ."registration.php?evid={$row['ev_id']}&regAgent={$row['registering_agent']}&agentWritingNum={$row['agent_writing_number']}&phone={$row['phone']}&email={$row['email']}&regSales={$row['regional_sales_coordinator']}&disSales={$row['district_sales_coordinator']}");
}
}
else if(isset($_POST['delete'])){ //for deleting events.  multiple events can be deleted
if(isset($_POST['events'])){ //MEMBER's personal registrations
	foreach($_POST['events'] as $value){
		$event = explode(", ", $value);

		$user_id = $event[0];
		$ev_id = $event[1];
		$login_timestamp = $event[2];

		$deleteTextQuery = "SELECT * FROM ". DB_PREFIX ."aflac WHERE user_id = '$user_id' AND ev_id = '$ev_id' AND login_timestamp = '$login_timestamp'";
		$deleteTextResults[] = dbquery($deleteTextQuery);

		$deleteAflacQuery = "DELETE FROM ". DB_PREFIX ."aflac WHERE user_id = '$user_id' AND ev_id = '$ev_id' AND login_timestamp = '$login_timestamp'";
		$deleteLoginsQuery = "DELETE FROM ". DB_PREFIX ."aw_ec_logins WHERE user_id = '$user_id' AND ev_id = '$ev_id' AND login_timestamp = '$login_timestamp'";

		$deleteAflacResult = dbquery($deleteAflacQuery);
		$deleteLoginsResult = dbquery($deleteLoginsQuery);
	}

	if($deleteAflacResult && $deleteLoginsResult){
		$deleteText = "The following registration(s) have been deleted:\n<br />\n<br />\n";
		$deleteText .= "<table cellspacing='0'>\n\t<tr>\n\t\t<th>Registering Agent</th><th>Event</th><th>Agent Writing Number</th><th>Phone Number</th><th>E-mail Address</th><th>Regional Sales Coordinator</th><th>District Sales Coordinator</th>\n\t</tr>";

		foreach($deleteTextResults as $queryResult){
			while($row = mysql_fetch_assoc($queryResult)){
				$deleteText .= "\n\t<tr class='regtbl1'>\n\t\t<td>{$row['registering_agent']}</td><td>{$row['ev_title']}</td><td>{$row['agent_writing_number']}</td><td>{$row['phone']}</td><td>{$row['email']}</td><td>{$row['regional_sales_coordinator']}</td><td>{$row['district_sales_coordinator']}</td>\n\t</tr>";
			}
		}

		$deleteText .= "\n</table>\n\n";
		$deleteText .= "<br /><br />\n\n<a href='news.php'>Return home</a> or <a href='registration_viewer.php'>modify another registration</a>";

		echo $deleteText;
	}					
}
else if(isset($_POST['personalEvents'])){ //ADMIN's personal registrations
	foreach($_POST['personalEvents'] as $value){
		$event = explode(", ", $value);

		$user_id = $event[0];
		$ev_id = $event[1];
		$login_timestamp = $event[2];

		$deleteTextQuery = "SELECT * FROM ". DB_PREFIX ."aflac WHERE user_id = '$user_id' AND ev_id = '$ev_id' AND login_timestamp = '$login_timestamp'";
		$deleteTextResults[] = dbquery($deleteTextQuery);

		$deleteAflacQuery = "DELETE FROM ". DB_PREFIX ."aflac WHERE user_id = '$user_id' AND ev_id = '$ev_id' AND login_timestamp = '$login_timestamp'";
		$deleteLoginsQuery = "DELETE FROM ". DB_PREFIX ."aw_ec_logins WHERE user_id = '$user_id' AND ev_id = '$ev_id' AND login_timestamp = '$login_timestamp'";

		$deleteAflacResult = dbquery($deleteAflacQuery);
		$deleteLoginsResult = dbquery($deleteLoginsQuery);
	}

	if($deleteAflacResult && $deleteLoginsResult){
		$deleteText = "The following registration(s) have been deleted:\n<br />\n<br />\n";
		$deleteText .= "<table cellspacing='0'>\n\t<tr>\n\t\t<th>Registering Agent</th><th>Event</th><th>Agent Writing Number</th><th>Phone Number</th><th>E-mail Address</th><th>Regional Sales Coordinator</th><th>District Sales Coordinator</th>\n\t</tr>";

		foreach($deleteTextResults as $queryResult){
			while($row = mysql_fetch_assoc($queryResult)){
				$deleteText .= "\n\t<tr class='regtbl1'>\n\t\t<td>{$row['registering_agent']}</td><td>{$row['ev_title']}</td><td>{$row['agent_writing_number']}</td><td>{$row['phone']}</td><td>{$row['email']}</td><td>{$row['regional_sales_coordinator']}</td><td>{$row['district_sales_coordinator']}</td>\n\t</tr>";
			}
		}

		$deleteText .= "\n</table>\n\n";
		$deleteText .= "<br /><br />\n\n<a href='news.php'>Return home</a> or <a href='registration_viewer.php'>modify another registration</a>";				

		echo $deleteText;
	}					
}
else if(isset($_POST['adminEvents'])){ //ADMIN: ALL possible registrations
	foreach($_POST['adminEvents'] as $value){
		$event = explode(", ", $value);

		$user_id = $event[0];
		$ev_id = $event[1];
		$login_timestamp = $event[2];

		$deleteTextQuery = "SELECT * FROM ". DB_PREFIX ."aflac WHERE user_id = '$user_id' AND ev_id = '$ev_id' AND login_timestamp = '$login_timestamp'";
		$deleteTextResults[] = dbquery($deleteTextQuery);

		$deleteAflacQuery = "DELETE FROM ". DB_PREFIX ."aflac WHERE user_id = '$user_id' AND ev_id = '$ev_id' AND login_timestamp = '$login_timestamp'";
		$deleteLoginsQuery = "DELETE FROM ". DB_PREFIX ."aw_ec_logins WHERE user_id = '$user_id' AND ev_id = '$ev_id' AND login_timestamp = '$login_timestamp'";

		$deleteAflacResult = dbquery($deleteAflacQuery);
		$deleteLoginsResult = dbquery($deleteLoginsQuery);
	}

	if($deleteAflacResult && $deleteLoginsResult){
		$deleteText = "The following registration(s) have been deleted:\n<br />\n<br />\n";
		$deleteText .= "<table cellspacing='0'>\n\t<tr>\n\t\t<th>Registering Agent</th><th>Event</th><th>Agent Writing Number</th><th>Phone Number</th><th>E-mail Address</th><th>Regional Sales Coordinator</th><th>District Sales Coordinator</th>\n\t</tr>";

		foreach($deleteTextResults as $queryResult){
			while($row = mysql_fetch_assoc($queryResult)){
				$deleteText .= "\n\t<tr class='regtbl1'>\n\t\t<td>{$row['registering_agent']}</td><td>{$row['ev_title']}</td><td>{$row['agent_writing_number']}</td><td>{$row['phone']}</td><td>{$row['email']}</td><td>{$row['regional_sales_coordinator']}</td><td>{$row['district_sales_coordinator']}</td>\n\t</tr>";
			}
		}

		$deleteText .= "\n</table>\n\n";
		$deleteText .= "<br /><br />\n\n<a href='news.php'>Return home</a> or <a href='registration_viewer.php'>modify another registration</a>";				

		echo $deleteText;
	}					
}
}

if(isset($_POST['personalSubmit'])){ //ADMIN's personal registrations
if(isset($_POST['personalEvents']) && $_POST['personalEvents'][0] == '*'){ //if ALL personal registrations are selected
	$personalQuery = "SELECT * FROM ". DB_PREFIX ."aflac WHERE user_id = '". $userdata['user_id'] ."' ORDER BY login_timestamp DESC";
	$personalResult = dbquery($personalQuery);

	echo "<span style='font-weight: bold; font-size: 1.35em;'>Your Registrations:</span><br /><br /><br />\n\n";
	echo "<form method='post' id='registrationForm' action='{$_SERVER['PHP_SELF']}'>\n";
	echo "<table cellspacing='0' cellpadding='5' class='tbl'>\n\t<tr class='regtbl2'>\n\t\t<th></th><th>Registering Agent</th><th>Event</th><th>Agent Writing Number</th><th>Phone Number</th><th>E-mail Address</th><th>Regional Sales Coordinator</th><th>District Sales Coordinator</th>\n\t</tr>";

	$count = 0;

	while($row = mysql_fetch_assoc($personalResult)){
		if($count % 2 == 0){
			echo "\n\t<tr class='regtbl1'>\n\t\t<td><input type='checkbox' name='personalEvents[]' value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}' /></td><td>{$row['registering_agent']}</td><td>{$row['ev_title']}</td><td>{$row['agent_writing_number']}</td><td>{$row['phone']}</td><td>{$row['email']}</td><td>{$row['regional_sales_coordinator']}</td><td>{$row['district_sales_coordinator']}</td>\n\t</tr>";
		}

		else{
			echo "\n\t<tr class='regtbl2'>\n\t\t<td><input type='checkbox' name='personalEvents[]' value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}' /></td><td>{$row['registering_agent']}</td><td>{$row['ev_title']}</td><td>{$row['agent_writing_number']}</td><td>{$row['phone']}</td><td>{$row['email']}</td><td>{$row['regional_sales_coordinator']}</td><td>{$row['district_sales_coordinator']}</td>\n\t</tr>";
		}

		$count++;
	}		

	echo "</table>\n<input type='submit' name='edit' value='Edit' /><input type='submit' name='delete' value='Delete' />\n</form>";
}
else if(isset($_POST['personalEvents']) && $_POST['personalEvents'][0] != '*'){ //for singular personal registrations
	$event = explode(", ", $_POST['personalEvents'][0]);

	$user_id = $event[0];
	$ev_id = $event[1];
	$login_timestamp = $event[2];

	$personalQuery = "SELECT * FROM ". DB_PREFIX ."aflac WHERE user_id = '$user_id' AND ev_id = '$ev_id' AND login_timestamp = '$login_timestamp' ORDER BY login_timestamp DESC";
	$personalResult = dbquery($personalQuery);

	echo "<span style='font-weight: bold; font-size: 1.35em;'>Your Registrations:</span><br /><br /><br />\n\n";		
	echo "<form method='post' id='registrationForm' action='{$_SERVER['PHP_SELF']}'>\n";
	echo "<table cellspacing='0' cellpadding='5' class='tbl'>\n\t<tr class='regtbl2'>\n\t\t<th></th><th>Registering Agent</th><th>Event</th><th>Agent Writing Number</th><th>Phone Number</th><th>E-mail Address</th><th>Regional Sales Coordinator</th><th>District Sales Coordinator</th>\n\t</tr>";

	while($row = mysql_fetch_assoc($personalResult)){
		echo "\n\t<tr class='regtbl1'>\n\t\t<td><input type='checkbox' name='personalEvents[]' value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}' /></td><td>{$row['registering_agent']}</td><td>{$row['ev_title']}</td><td>{$row['agent_writing_number']}</td><td>{$row['phone']}</td><td>{$row['email']}</td><td>{$row['regional_sales_coordinator']}</td><td>{$row['district_sales_coordinator']}</td>\n\t</tr>";
	}

	echo "</table>\n<input type='submit' name='edit' value='Edit' /><input type='submit' name='delete' value='Delete' />\n</form>";
}
}

if(isset($_POST['submit'])){ //non-ADMIN's personal registrations
if(isset($_POST['events']) && $_POST['events'][0] == '*'){ //if ALL personal registrations are selected
	$personalQuery = "SELECT * FROM ". DB_PREFIX ."aflac WHERE user_id = '". $userdata['user_id'] ."' ORDER BY login_timestamp DESC";
	$personalResult = dbquery($personalQuery);

	echo "<span style='font-weight: bold; font-size: 1.35em;'>Your Registrations:</span><br /><br /><br />\n\n";		
	echo "<form method='post' id='registrationForm' action='{$_SERVER['PHP_SELF']}'>\n";
	echo "<table cellspacing='0' cellpadding='5' class='tbl'>\n\t<tr class='regtbl2'>\n\t\t<th></th><th>Registering Agent</th><th>Event</th><th>Agent Writing Number</th><th>Phone Number</th><th>E-mail Address</th><th>Regional Sales Coordinator</th><th>District Sales Coordinator</th>\n\t</tr>";

	$count = 0;

	while($row = mysql_fetch_assoc($personalResult)){
		if($count % 2 == 0){
			echo "\n\t<tr class='regtbl1'>\n\t\t<td><input type='checkbox' name='events[]' value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}' /></td><td>{$row['registering_agent']}</td><td>{$row['ev_title']}</td><td>{$row['agent_writing_number']}</td><td>{$row['phone']}</td><td>{$row['email']}</td><td>{$row['regional_sales_coordinator']}</td><td>{$row['district_sales_coordinator']}</td>\n\t</tr>";
		}
		else{
			echo "\n\t<tr class='regtbl2'>\n\t\t<td><input type='checkbox' name='events[]' value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}' /></td><td>{$row['registering_agent']}</td><td>{$row['ev_title']}</td><td>{$row['agent_writing_number']}</td><td>{$row['phone']}</td><td>{$row['email']}</td><td>{$row['regional_sales_coordinator']}</td><td>{$row['district_sales_coordinator']}</td>\n\t</tr>";
		}

		$count++;
	}	

	echo "</table>\n<input type='submit' name='edit' value='Edit' /><input type='submit' name='delete' value='Delete' />\n</form>";
}
else if(isset($_POST['events']) && $_POST['events'][0] != '*'){ //for singular personal registrations
	$event = explode(", ", $_POST['events'][0]);

	$user_id = $event[0];
	$ev_id = $event[1];
	$login_timestamp = $event[2];

	$personalQuery = "SELECT * FROM ". DB_PREFIX ."aflac WHERE user_id = '$user_id' AND ev_id = '$ev_id' AND login_timestamp = '$login_timestamp' ORDER BY login_timestamp DESC";
	$personalResult = dbquery($personalQuery);

	echo "<span style='font-weight: bold; font-size: 1.35em;'>Your Registrations:</span><br /><br /><br />\n\n";		
	echo "<form method='post' id='registrationForm' action='{$_SERVER['PHP_SELF']}'>\n";
	echo "<table cellspacing='0' cellpadding='5' class='tbl'>\n\t<tr class='regtbl2'>\n\t\t<th></th><th>Registering Agent</th><th>Event</th><th>Agent Writing Number</th><th>Phone Number</th><th>E-mail Address</th><th>Regional Sales Coordinator</th><th>District Sales Coordinator</th>\n\t</tr>";

	while($row = mysql_fetch_assoc($personalResult)){
		echo "\n\t<tr class='regtbl1'>\n\t\t<td><input type='checkbox' name='events[]' value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}' /></td><td>{$row['registering_agent']}</td><td>{$row['ev_title']}</td><td>{$row['agent_writing_number']}</td><td>{$row['phone']}</td><td>{$row['email']}</td><td>{$row['regional_sales_coordinator']}</td><td>{$row['district_sales_coordinator']}</td>\n\t</tr>";
	}

	echo "</table>\n<input type='submit' name='edit' value='Edit' /><input type='submit' name='delete' value='Delete' />\n</form>";
}
}

if(isset($_POST['adminSubmit'])){ //ADMIN-only: ALL possible event registrations
if(isset($_POST['adminEvents']) && $_POST['adminEvents'][0] == '*'){ //if ALL registrations are selected
	$adminQuery = "SELECT * FROM ". DB_PREFIX ."aflac ORDER BY login_timestamp DESC";
	$adminResult = dbquery($adminQuery);

	echo "<span style='font-weight: bold; font-size: 1.35em;'>All Member Registrations:</span><br /><br /><br />\n\n";
	echo "<form method='post' id='registrationForm' action='{$_SERVER['PHP_SELF']}'>\n";
	echo "<table cellspacing='0' cellpadding='5' class='tbl'>\n\t<tr class='regtbl2'>\n\t\t<th></th><th>Registering Agent</th><th>Event</th><th>Agent Writing Number</th><th>Phone Number</th><th>E-mail Address</th><th>Regional Sales Coordinator</th><th>District Sales Coordinator</th>\n\t</tr>";

	$count = 0;

	while($row = mysql_fetch_assoc($adminResult)){
		if($count % 2 == 0){
			echo "\n\t<tr class='regtbl1'>\n\t\t<td><input type='checkbox' name='adminEvents[]' value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}' /></td><td>{$row['registering_agent']}</td><td>{$row['ev_title']}</td><td>{$row['agent_writing_number']}</td><td>{$row['phone']}</td><td>{$row['email']}</td><td>{$row['regional_sales_coordinator']}</td><td>{$row['district_sales_coordinator']}</td>\n\t</tr>";
		}			
		else{
			echo "\n\t<tr class='regtbl2'>\n\t\t<td><input type='checkbox' name='adminEvents[]' value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}' /></td><td>{$row['registering_agent']}</td><td>{$row['ev_title']}</td><td>{$row['agent_writing_number']}</td><td>{$row['phone']}</td><td>{$row['email']}</td><td>{$row['regional_sales_coordinator']}</td><td>{$row['district_sales_coordinator']}</td>\n\t</tr>";
		}

		$count++;
	}

	echo "</table>\n<input type='submit' name='delete' value='Delete' />\n</form>";
}
else if(isset($_POST['adminEvents']) && $_POST['adminEvents'][0] != '*'){ //for singular registrations
	$event = explode(", ", $_POST['adminEvents'][0]);

	$user_id = $event[0];
	$ev_id = $event[1];
	$login_timestamp = $event[2];

	$adminQuery = "SELECT * FROM ". DB_PREFIX ."aflac WHERE ev_id = '$ev_id' AND login_timestamp = '$login_timestamp' ORDER BY login_timestamp DESC";
	$adminResult = dbquery($adminQuery);

	echo "<span style='font-weight: bold; font-size: 1.35em;'>One Member Registration:</span><br /><br /><br />\n\n";		
	echo "<form method='post' id='registrationForm' action='{$_SERVER['PHP_SELF']}'>\n";
	echo "<table cellspacing='0' cellpadding='5' class='tbl'>\n\t<tr class='regtbl2'>\n\t\t<th></th><th>Registering Agent</th><th>Event</th><th>Agent Writing Number</th><th>Phone Number</th><th>E-mail Address</th><th>Regional Sales Coordinator</th><th>District Sales Coordinator</th>\n\t</tr>";

	while($row = mysql_fetch_assoc($adminResult)){
		echo "\n\t<tr class='regtbl1'>\n\t\t<td><input type='checkbox' name='adminEvents[]' value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}' /></td><td>{$row['registering_agent']}</td><td>{$row['ev_title']}</td><td>{$row['agent_writing_number']}</td><td>{$row['phone']}</td><td>{$row['email']}</td><td>{$row['regional_sales_coordinator']}</td><td>{$row['district_sales_coordinator']}</td>\n\t</tr>";
	}

	echo "</table>\n<input type='submit' name='delete' value='Delete' />\n</form>";
}
}

require_once "side_right.php";
require_once "footer.php";

?>

 

As you can see, the code -- especially registration_controller's -- is pretty messy.  It all works, and works correctly, but it is anything but readable and easy to maintain.  It is my belief that it could use an OOP makeover.

 

The following posts will be something of a brainstorming session.  Since I'm learning as I go, I'll be throwing ideas out that, hopefully, the vets will be able to help me refine.  My next post will start the process of rebuilding registration_viewer.

Link to comment
https://forums.phpfreaks.com/topic/60399-case-study-from-procedural-to-oop/
Share on other sites

Transforming registration_viewer

 

NOTE: all posts from now on will ignore the PHP-Fusion includes that display that software's chrome (so maincore.php, side_left.php, etc).

 

In my study, I've learned that one of the tenets of OOP is that conditionals (especially repeated/parallel conditionals) are bad.  So, naturally, the first thing I've done is look at the conditionals.  Registration_viewer, in this case, isn't bad.  There are three conditionals:

<?php

if(!iMEMBER){
   fallback();
}

?>

 

This conditional checks, using one of PHP-Fusion's built-in constants, if the user trying to access the script is a registered member of the site.  If not, the user is redirected to the index of the site.

 

<?php

if(iADMIN){
   //do stuff
}

?>

 

This conditional checks to see if the user is a site administrator.  This is important because, like I said in the first post, administrators have edit/delete options that normal members do not.

 

<?php

if(iMEMBER && !iADMIN){
   //do stuff
}

?>

 

This conditional checks to see that the user is just a normal member.

 

Now, I'm thinking that I can make some sort of viewer class, something like:

<?php

class RegistrationViewer{
   protected $status;

   public function __construct(){
      switch($userdata['user_level']){ //built-in PHP-Fusion array
         case 101:
            return new MemberViewer();
            break;
         case 102:
         case 103:
            return new AdminViewer();
            break;
         default:
             echo "You're not a registered user!";
             return false;
      }
   }
}

class AdminViewer extends RegistrationViewer{
   public function __construct(){
      $status = 'iADMIN';
   }
}

class MemberViewer extends RegistrationViewer{
   public function __construct(){
      $status = 'iMEMBER';
   }
}

?>

 

With this, polymorphism takes the place of the conditionals.  Obviously, I must give the classes something to do, but right now I'm just focused on the preliminary setup.

 

I'm thinking of doing something along the lines of:

<?php

class RegistrationViewer{
   protected $status;

   public function __construct(){
      switch($userdata['user_level']){ //built-in PHP-Fusion array
         case 101:
            return new MemberViewer();
            break;
         case 102:
         case 103:
            return new AdminViewer();
            break;
         default:
             echo "You're not a registered user!";
             return false;
      }
   }

   abstract function makeMenu();
}

class AdminViewer extends RegistrationViewer{
   public function __construct(){
      $status = 'iADMIN';
   }

   public function makeMenu(){
      //make the admin menus
   }
}

class MemberViewer extends RegistrationViewer{
   public function __construct(){
      $status = 'iMEMBER';
   }

   public function makeMenu(){
      //make the member menu
   }
}

?>

 

Do I have the syntax right?  Actually, more to the point, am I on the right track so far?

So, now for the next step: writing all of my viewer classes.  Before I do that, though, I need to figure out what to do with the forms' (that will be built via makeMenu) actions.

 

I believe that keeping the scripts seperate will be the most efficient way to handle things.  That way, I can use a Factory to create whatever objects I need on that page out of the info that's passed to it by POST.

 

For the viewer classes themselves, there's an added wrinkle.  There are TWO lists that must be made for site administrators.  I could just copy what I have in my old script to the makeMenu() function, but I'm not sure if that's really OOP-ish.  On the one hand, both lists use the same form.  On the other, everything else about them is seperate, even their respective inputs.  I'm leaning towards separating the two processes, but I'm not sure how to incorporate them into the makeMenu() function.  Any vets have any ideas on this?

 

In any event, MemberViewer is ready to be completed as it only has one list to make.

 

<?php

class RegistrationViewer{
   protected $status;

   public function __construct(){
      switch($userdata['user_level']){ //built-in PHP-Fusion array
         case 101:
            return new MemberViewer();
            break;
         case 102:
         case 103:
            return new AdminViewer();
            break;
         default:
             echo "You're not a registered user!";
             return false;
      }
   }

   abstract function makeMenu();
}

class AdminViewer extends RegistrationViewer{
   public function __construct(){
      $this -> status = 'iADMIN';
   }

   public function makeMenu(){
      //do stuff
   }
}

class MemberViewer extends RegistrationViewer{
   public function __construct(){
      $this -> status = 'iMEMBER';
   }

   public function makeMenu(){
      $formText = "";

      $query = "SELECT * FROM ". DB_PREFIX ."aflac WHERE user_id = '". $userdata['user_id'] ."'";
      $result = dbquery($query);

      $formText .= "<form method='POST' action='registration_controller.php'>\n<br />\n";
      $formText .= "Your registrations:<br /><br />\n";
      $formText .= "<select name='events[]'>\n<br />\n";
      $formText .= "<option value='*'>All</option>\n<br />\n";

      while($row = mysql_fetch_assoc($result)){
         $formText .= "<option value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}'>{$row['ev_title']} -- ". date('M j, Y', $row['login_timestamp']) ."</option>\n<br />\n";
      }

      $formText .= "</select>\n";
      $formText .= "<input type='submit' name='submit' value='Go' />\n<br />\n</form>\n<br />\n";

      return $formText;
   }
}

?>

 

As you can see, the process remains the same as it was in the old script, with the exception that all of the echoed text is now returned as a large string.  There isn't any error checking with the database as any empty queries create empty lists in the form, letting the user see that they haven't registered for anything yet.  And yes, I know that my constructor code changed slightly...I forgot to add $this to the first versions.  Oops! :)

Now that I've got my classes somewhat constructed (the obvious exception being AdminViewer), it's time to test it out.

 

So, here's my test script (regviewertest.php5):

<?php

require_once "maincore.php";
require_once "subheader.php";
require_once "side_left.php";
require_once "regclasses.php5";

$viewer = new RegistrationViewer();

$menu = $viewer->makeMenu();

echo "$menu";

require_once "side_right.php";
require_once "footer.php";

?>

 

Sexy, huh?  The required regclasses.php5 file is, obviously, the file containing my classes.

 

So, I fire up the script and get the following message:

Fatal error: Class RegistrationViewer contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (RegistrationViewer::makeMenu) in /home/nights/www/www/regclasses.php5 on line 22

 

I didn't realize that abstract methods weren't allowed in normal classes.  It's a nice little 'gotcha' to remember.  So, I modify my classes to be the following:

<?php

class RegistrationViewer{
   protected $status;

   public static function getInstance(){
      switch($userdata['user_level']){ //built-in PHP-Fusion array
         case 101:
            return new MemberViewer();
            break;
         case 102:
         case 103:
            return new AdminViewer();
            break;
         default:
             echo "You're not a registered user!";
             return false;
      }
   }
}

/* class AdminViewer extends RegistrationViewer{
   public function __construct(){
      $this -> status = 'iADMIN';
   }

   public function makeMenu(){
      //do stuff
   }
} */

class MemberViewer extends RegistrationViewer{
   public function __construct(){
      $this -> status = 'iMEMBER';
   }

   public function makeMenu(){
      $formText = "";

      $query = "SELECT * FROM ". DB_PREFIX ."aflac WHERE user_id = '". $userdata['user_id'] ."'";
      $result = dbquery($query);

      $formText .= "<form method='POST' action='registration_controller.php'>\n<br />\n";
      $formText .= "Your registrations:<br /><br />\n";
      $formText .= "<select name='events[]'>\n<br />\n";
      $formText .= "<option value='*'>All</option>\n<br />\n";

      while($row = mysql_fetch_assoc($result)){
         $formText .= "<option value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}'>{$row['ev_title']} -- ". date('M j, Y', $row['login_timestamp']) ."</option>\n<br />\n";
      }

      $formText .= "</select>\n";
      $formText .= "<input type='submit' name='submit' value='Go' />\n<br />\n</form>\n<br />\n";

      return $formText;
   }
}

 

And my script has changed slightly in order to use the static function:

<?php

require_once "maincore.php";
require_once "subheader.php";
require_once "side_left.php";
require_once "regclasses.php5";

$viewer = RegistrationViewer::getInstance();

$menu = $viewer->makeMenu();

echo "$menu";

require_once "side_right.php";
require_once "footer.php";

?>

 

Unfortunately, we lose the makeMenu() function from the base class.  But since I want to rely on polymorphism to return the correct object, I believe that's the only thing I could do.  Upon the next test, I get the following output:

You're not a registered user!

Fatal error: Call to a member function makeMenu() on a non-object in /home/nights/www/www/regviewertest.php5 on line 12

 

It looks like my parent class isn't recognizing that my current user level is indeed 101 (which I double-checked in the script by echoing $userdata['user_level']).  Because of this, $viewer = false.  That, in turn, means that it can't call makeMenu() because it's not an object.  The worst part is, however, is that I'm not sure why the logic error is occurring as my syntax looks to be correct and, as I said above, my user level is 101.  I should be getting a MemberViewer object back.

 

So, any vets mind helping me out with this error?  I'm at a bit of a loss. :)

$viewer is not an object. That's why you cant call makeMenu().

 

I know that.  $viewer SHOULD be an object, though, as getInstance is supposed to return an object, its type determined by the user's user level.  In my test script, it's supposed to be a MemberViewer object.

With a little help of someone on another forum, I've gotten my test to work.

 

The problem I had:

For some reason, the global array $userdata didn't work right within my classes. 

 

The solution:

Removing direct calls to it solved that issue, and, as a positive side effect, made my class code less reliant on its environment.

 

I've refined my classes a bit more:

There's a new property, $userId, which is used in database calls.

 

I've made both $status and $userId static as they should remain the same throughout the class' lifetimes.  I didn't make $status const mainly because I hate dealing with PHP consts.

 

I've added a debugging method.

 

Class code:

<?php

class RegistrationViewer{
   protected static $status;
   protected static $userId;

   public static function getInstance($userLevel, $userId){
      switch($userLevel){
         case 101:
            return new MemberViewer('iMEMBER', $userId);
            break;
         case 102:
         case 103:
            return new AdminViewer('iADMIN', $userId);
            break;
         default:
             echo "You're not a registered user!";
             return false;
      }
   }

   public function printStatus(){ //for debugging
      echo 'User level: '. $this->status .'<br />User ID: '. $this->userId .'<br /><br />';
   }
}

/* class AdminViewer extends RegistrationViewer{
   public function __construct($status, $userId){
      $this->status = $status;
      $this->userId = $userId;
   }

   public function makeMenu(){
      do stuff
   }
} */

class MemberViewer extends RegistrationViewer{
   public function __construct($status, $userId){
      $this->status = $status;
      $this->userId = $userId;
   }

   public function makeMenu(){
      $formText = "";

      $query = "SELECT * FROM ". DB_PREFIX ."aflac WHERE user_id = '". $this->userId ."'";
      $result = dbquery($query);

      $formText .= "<form method='POST' action='registration_controller.php'>\n<br />\n";
      $formText .= "Your registrations:<br /><br />\n";
      $formText .= "<select name='events[]'>\n<br />\n";
      $formText .= "<option value='*'>All</option>\n<br />\n";

      while($row = mysql_fetch_assoc($result)){
         $formText .= "<option value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}'>{$row['ev_title']} -- ". date('M j, Y', $row['login_timestamp']) ."</option>\n<br />\n";
      }

      $formText .= "</select>\n";
      $formText .= "<input type='submit' name='submit' value='Go' />\n<br />\n</form>\n<br />\n";

      echo $formText;
   }
}

?>

 

Test script:

<?php

require_once "maincore.php";
require_once "subheader.php";
require_once "side_left.php";
require_once "regclasses.php5";

$viewer = RegistrationViewer::getInstance($userdata['user_level'], $userdata['user_id']);

$viewer->printStatus();

$menu = $viewer->makeMenu();

require_once "side_right.php";
require_once "footer.php";

?>

You may want to create a generic user object.

 

<?php
class User {

  public $userId;
  public $userLevel;

  function User($userId) {
        $this->userId = $userId;
  }

.
.
.
.


}
?>

 

That way, you can turn your code to look something like this:

 

Class code:

 

<?php

class RegistrationViewer{
   protected static $status;
   protected static $userId;

   public static function getInstance(User $user){
      switch($user->userLevel){
         case 101:
            return new MemberViewer('iMEMBER', $user->userId);
            break;
         case 102:
         case 103:
            return new AdminViewer('iADMIN', $user->userId);
            break;
         default:
             echo "You're not a registered user!";
             return false;
      }
   }

   public function printStatus(){ //for debugging
      echo 'User level: '. $this->status .'<br />User ID: '. $this->userId .'<br /><br />';
   }
}

/* class AdminViewer extends RegistrationViewer{
   public function __construct($status, $userId){
      $this->status = $status;
      $this->userId = $userId;
   }

   public function makeMenu(){
      do stuff
   }
} */

class MemberViewer extends RegistrationViewer{
   public function __construct($status, $userId){
      $this->status = $status;
      $this->userId = $userId;
   }

   public function makeMenu(){
      $formText = "";

      $query = "SELECT * FROM ". DB_PREFIX ."aflac WHERE user_id = '". $this->userId ."'";
      $result = dbquery($query);

      $formText .= "<form method='POST' action='registration_controller.php'>\n<br />\n";
      $formText .= "Your registrations:<br /><br />\n";
      $formText .= "<select name='events[]'>\n<br />\n";
      $formText .= "<option value='*'>All</option>\n<br />\n";

      while($row = mysql_fetch_assoc($result)){
         $formText .= "<option value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}'>{$row['ev_title']} -- ". date('M j, Y', $row['login_timestamp']) ."</option>\n<br />\n";
      }

      $formText .= "</select>\n";
      $formText .= "<input type='submit' name='submit' value='Go' />\n<br />\n</form>\n<br />\n";

      echo $formText;
   }
}

?>

 

 

Just a suggestion :)

 

 

Good idea. :)  I just implemented that now:

<?php

class User{
   private $userLevel;
   private $userId;

   public function __construct($userLevel, $userId){
      $this->userLevel = $userLevel;
      $this->userId = $userId;
   }

   public function getUserLevel(){
      return $this->userLevel;
   }

   public function getUserId(){
      return $this->userId;
   }
}

class RegistrationViewer{
   protected static $status;
   protected static $userId;

   public static function getInstance(User $user){
      switch($user->getUserLevel()){
         case 101:
            return new MemberViewer('iMEMBER', $user->getUserId());
            break;
         case 102:
         case 103:
            return new AdminViewer('iADMIN', $user->getUserId());
            break;
         default:
             echo "You're not a registered user!";
             return false;
      }
   }

   public function printStatus(){
      echo 'User level: '. $this->status .'<br />User ID: '. $this->userId .'<br /><br />';
   }
}

/* class AdminViewer extends RegistrationViewer{
   public function __construct($status, $userId){
      $this->status = $status;
      $this->userId = $userId;
   }

   public function makeMenu(){
      do stuff
   }
} */

class MemberViewer extends RegistrationViewer{
   public function __construct($status, $userId){
      $this->status = $status;
      $this->userId = $userId;
   }

   public function makeMenu(){
      $formText = "";

      $query = "SELECT * FROM ". DB_PREFIX ."aflac WHERE user_id = '". $this->userId ."'";
      $result = dbquery($query);

      $formText .= "<form method='POST' action='registration_controller.php'>\n<br />\n";
      $formText .= "Your registrations:<br /><br />\n";
      $formText .= "<select name='events[]'>\n<br />\n";
      $formText .= "<option value='*'>All</option>\n<br />\n";

      while($row = mysql_fetch_assoc($result)){
         $formText .= "<option value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}'>{$row['ev_title']} -- ". date('M j, Y', $row['login_timestamp']) ."</option>\n<br />\n";
      }

      $formText .= "</select>\n";
      $formText .= "<input type='submit' name='submit' value='Go' />\n<br />\n</form>\n<br />\n";

      echo $formText;
   }
}

?>

 

And the test script is:

<?php

require_once "maincore.php";
require_once "subheader.php";
require_once "side_left.php";
require_once "regclasses.php5";

$currentUser = new User($userdata['user_level'], $userdata['user_id']);

$viewer = RegistrationViewer::getInstance($currentUser);
$viewer->makeMenu();

require_once "side_right.php";
require_once "footer.php";

?>

 

I feel I should pause and explain the benefits of this method of coding to any newbies reading in case it's not obvious.

 

The functionality is more or less the same as it was in the procedural script.  The difference, however, is that it's sequestered away into classes and objects.  The only real OOP-ish component, at this point, is how getInstance() works.  It automatically returns the correct type of RegistrationViewer object at runtime as it's derived from what kind of User is currently trying to access the page.

 

The benefits are most appearent in the actual test script.  Rather than having to put all of the form-creation process in the script itself, and repeat it where ever I want the form, I can just create a new object and call makeMenu().  The form is, at least in the realm of PHP-Fusion, modular.

 

Up next: AdminViewer's makeMenu()!

I made the decision to break AdminViewer's makeMenu() method into smaller chunks -- one to deal with an administrator's personal event registrations, the other to deal with ALL event registrations.  This allows me to keep the generic makeMenu() method call, but with specific things occurring in the background:

<?php

class User{
   private $userLevel;
   private $userId;

   public function __construct($userLevel, $userId){
      $this->userLevel = $userLevel;
      $this->userId = $userId;
   }

   public function getUserLevel(){
      return $this->userLevel;
   }

   public function getUserId(){
      return $this->userId;
   }
}

class RegistrationViewer{
   protected static $status;
   protected static $userId;

   public static function getInstance(User $user){
      switch($user->getUserLevel()){
         case 101:
            return new MemberViewer('iMEMBER', $user->getUserId());
            break;
         case 102:
         case 103:
            return new AdminViewer('iADMIN', $user->getUserId());
            break;
         default:
             echo "You're not a registered user!";
             return false;
      }
   }

   public function printStatus(){
      echo 'User level: '. $this->status .'<br />User ID: '. $this->userId .'<br /><br />';
   }
}

class AdminViewer extends RegistrationViewer{
   public function __construct($status, $userId){
      $this->status = $status;
      $this->userId = $userId;
   }

   public function makeMenu(){
      echo $this->makePersonalMenu() . $this->makeAdminMenu();
   }

   public function makePersonalMenu(){
      $formText = "";

      $query = "SELECT * FROM ". DB_PREFIX ."aflac WHERE user_id = '". $this->userId ."'";
      $result = dbquery($query);

      $formText .= "<form method='POST' action='registration_controller.php'>\n<br />\n";
      $formText .= "Your registrations: <br /><br />\n";
      $formText .= "<select name='personalEvents[]'>\n<br />\n";
      $formText .= "<option value='*'>All</option>\n<br />\n";

      while($row = mysql_fetch_assoc($result)){
         $formText .= "<option value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}'>{$row['ev_title']} -- ". date('M j, Y', $row['login_timestamp']) ."</option>\n<br />\n";
      }

      $formText .= "</select>\n";
      $formText .= "<input type='submit' name='personalSubmit' value='Go' />\n<br />\n<br /><br /><br /><br />\n";

      return $formText;
   }

   public function makeAdminMenu(){
      $formText = "";

      $query = "SELECT * FROM ". DB_PREFIX ."aflac";
      $result = dbquery($query);

      $formText .= "All registrations: <br /><br />\n";
      $formText .= "<select name='adminEvents[]'>\n<br />\n";
      $formText .= "<option value='*'>All</option>\n<br />\n";

      while($row = mysql_fetch_assoc($result)){
         $formText .= "<option value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}'>{$row['registering_agent']}: {$row['ev_title']} -- ". date('M j, Y', $row['login_timestamp']) ."</option>\n<br />\n";
      }

      $formText .= "</select>\n";
      $formText .= "<input type='submit' name='adminSubmit' value='Go' />\n<br />\n<br />\n</form>\n";

      return $formText;
   }
}

class MemberViewer extends RegistrationViewer{
   public function __construct($status, $userId){
      $this->status = $status;
      $this->userId = $userId;
   }

   public function makeMenu(){
      $formText = "";

      $query = "SELECT * FROM ". DB_PREFIX ."aflac WHERE user_id = '". $this->userId ."'";
      $result = dbquery($query);

      $formText .= "<form method='POST' action='registration_controller.php'>\n<br />\n";
      $formText .= "Your registrations:<br /><br />\n";
      $formText .= "<select name='events[]'>\n<br />\n";
      $formText .= "<option value='*'>All</option>\n<br />\n";

      while($row = mysql_fetch_assoc($result)){
         $formText .= "<option value='{$row['user_id']}, {$row['ev_id']}, {$row['login_timestamp']}'>{$row['ev_title']} -- ". date('M j, Y', $row['login_timestamp']) ."</option>\n<br />\n";
      }

      $formText .= "</select>\n";
      $formText .= "<input type='submit' name='submit' value='Go' />\n<br />\n</form>\n<br />\n";

      echo $formText;
   }
}

?>

 

Now that this all working, the hard part begins -- transforming registration_controller.

 

Before we dive right in, we must look at how the Viewer classes create their respective forms.  In each case, the forms' select elements each have a different name.  Each submit button also has a different name corresponding to those select element names.  How can I create a Controller object that can accept any of the Viewers' info and return the correct data from the table, as well as edit and delete the correct items?  Is this even possible?

 

The problem hinges on AdminViewer.  It creates one form, but it has TWO submit buttons, and TWO different lists of information.  I think that creating a specific Member/AdminController behind a generic RegistrationController facade (much like Member/AdminViewer was created by the generic RegistrationViewer) is the way to go.  The structure will be similar to what I've already built, and it will give the Controllers some modularity (at least, in the context of PHP-Fusion).

 

EDIT: just thought of something.  The original script is a sticky form.  Is there any way for me to emulate that functionality within a class?

Can you explain what you mean by a "sticky form?"

 

A sticky form is a form that will display the info the user input (in the correct form fields) upon an unsuccessful form submission.  This is a convenience for the user as it makes it so they don't need to re-enter all of their info if the submission fails.  So something like:

<input type="text" name="username" value="<?php if(isset($_POST['username'])){echo $_POST['username'];} ?>" />

 

Now that I think about it more, though, I don't need a sticky form as the only input is only a series of checkboxes.  What I do need, however, is this:  My current controller forms use their own scripts as the forms' action.

<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="POST">
.
.
.
</form>

 

I'm not sure how to do the same thing in an OOP manner.  Is there an OOP way to do this?  Or are forms like this typically handled in a procedural manner?

You could do something like this

 

<?php

abstract class Form {

   abstract function addItem(Item $item, $value)
   abstract function draw()

}

abstract class Item {
  abstract function toHTML(); // abstract this to items like button, input, textarea, etc

}

class StickyForm extends Form {
    private $itemList;

    function addItem(Item $item, $value=NULL) {
      $this->itemList[] += array($item, value);
    }

    function draw() {
       foreach ($this->itemList as $item) {
          $obj = $item[0];
          $obj->toHTML($item[1]);
       }
    }
}
?>

 

 

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.