Jump to content

Recommended Posts

I've been trying to improve the quality of my code design, an improvement I feel is overdue.  In my defense, I learned programming self-taught and before OOP was around.  While I understand inherited classes and all that well enough to use them, I don't really have any good practices for planning them.

 

So here's what I'm struggling with.  The database is frequently the choke point of any application, and so I want to use it as frequently as possible.  What I've read is that this means not requesting excess data and avoiding excess calls.  But those are at odds with each other.  Suppose I have a hypothetical database with tables like this:

 

User_Tbl (UserID*, Login, Password, FullName, Address)

Cars_Tbl (CarID*, UserID, Make, Model, Year, Color, Value)

 

And one of my primary uses will be showing a page for a selected car, with the owner's info there too.  So I can easily pull all this data out in a single query by Inner Join on UserID.  One query, gets everything I need and nothing extra, very efficient.

 

But let's say this is a big application with a hundred other pages.  And I don't need all this data on every page.  Sometimes I just want the Value.  Sometimes I want just Address and Model.  (Nevermind why, I just do.)  Somewhere in these hundred pages every conceivable combination is at some point exactly what I want.  And not just reading, writing too.

 

So it seems to me when designing this class that I have 3 options:

1) Whenever I need any car data I run that same joined query.  It's wasteful because it retrieves information I often won't need.  Sometimes from a whole table I didn't need.

 

2) Run a query separate for each column I need.  Wasteful because it turns the primary case from 1 query into 10.

 

3) Create a separate whatever for each case, so the database is always queried efficiently.  Wasteful because none of the code is re-used.

 

How do people solve this?  My attempts to research so far led me into a mess of descriptions over value objects and transfer objects and singletons and factories and other design patterns that never really approach the actual question.  I know how to write what I want, I just don't know what I should want.

 

I fear the answer is going to be something like "it varies" but I don't know how to analyze it when I don't have a basis of best practices.  And my old bad habits whisper to not even bother putting the data into an object since it will only result in inefficiency.  Any advice or good references?

 

Thanks in advance.

 

(Again, the example is completely made up for illustration purposes.  Please don't get caught up in why a need for odd data combinations would exist.  A real example would have 20 tables of 20 columns each and just exacerbate the problem.)

 

By the way I did read the recent thread here and it's very interesting but didn't seem quite the same as what I'm asking.  Or it is and I didn't understand.

1) Whenever I need any car data I run that same joined query.  It's wasteful because it retrieves information I often won't need.  Sometimes from a whole table I didn't need.

 

Make sure you always retrieve only the data that you need in the request nothing more, nothing less (SELECT field1, field2 FROM). If certain queries return a big result and these results do not change often consider caching.

 

How do people solve this?

 

Solve what? To create efficient queries?

 

Give me a real example so that I can give you a real solution.

 

PS that thread wasn't really relevant as it discusses design patterns and directory layout's none of which is applicable to databases

So you're telling me to never use Value Objects to mirror DB tables.  By definition they match the whole table, and I don't need the whole table in most cases, so the whole layer is utterly worthless.

 

That's reassuring.  It's what I've been doing for 15 years.  Not what I expected though.  I thought the whole J2EE-inspired approach these days was to keep putting in layers of abstraction to ensure reuseability.

Not applicable as in nothing to do with queries or your database design structure. PHP and Java are very different as PHP only "lives" for as long as a request and how you model your application is entirely up to you.

 

Post some code you want to shed some more light on so far you provided no example just vague descriptions.

Not applicable as in nothing to do with queries or your database design structure.
The question wasn't about database design structure.  The question is about class design structure for interfacing with the database.  But apparently I wasn't clear so I'll try again and get more specific.

 

I'll lay out code as I might write it.  Then I'll say why I don't like it, and hopefully you can tell me how it could be done better.

 

First, let me redefine the tables:

 

Users_Tbl (UserID*, Login, Password, FullName, Address, NetWorth)

Cars_Tbl (CarID*, UserID, Make, Model, Year, Color, Value)

Boats_Tbl (BoatID*, UserID, Make, Model, Size, Year, Color, Value)

 

 

inc/DisplayFunctions.inc:
<?php
function formatDescription ($Color, $Year, $Make, $Model) {
  return "($Color) $Year $Make $Model";
}
?>

 

 lib/includedClasses.inc:
<?php
include_once("lib/DB_Handler.inc"); // Wraps db
include_once("lib/ValuationFunctions.inc"); // Assesses value based on characteristics
include_once("lib/DisplayFunctions.inc");

class User {
  private $UserID, $FullName, $Address, $NetWorth;
  
  __construct ($UserID) {
    $DB_Result = new db_query("SELECT FullName, Address, NetWorth FROM Users_Tbl WHERE UserID = $UserID");
    $Row = $DB_Result->Fetch();
    $this->UserID = $UserID;
    $this->FullName = $Row['FullName'];
    $this->Address = $Row['Address'];
    $this->NetWorth = $Row['NetWorth'];
  }

  function showUser() {
    return $this->UserID;
  }
  function showFullName() {
    return $this->FullName;
  }
  function showAddress() {
    return $this->Address;
  }
  function showNetWorth() {
    return $this->NetWorth;
  }
  
  function addCar($Make, $Model, $Year, $Color, $Value) {
    $DB_Result1 = new db_query("INSERT INTO Cars_Tbl (UserID, Make, Model, Year, Color, Value) VALUES ($this->UserID, $Make, $Model, $Year, $Color, $Value)");
    $this->NetWorth = $this->NetWorth + $Value;
    $DB_Result2 = new db_query("UPDATE Users_Tbl SET NetWorth = NetWorth + $Value WHERE UserID = $this->UserID");
    return $DB_Result1;
  }

  function addBoat($Make, $Model, $Size, $Year, $Color, $Value) {
    $DB_Result1 = new db_query("INSERT INTO Boats_Tbl (UserID, Make, Model, Size, Year, Color, Value) VALUES ($this->UserID, $Make, $Model, $Size, $Year, $Color, $Value)");
    $this->NetWorth = $this->NetWorth + $Value;
    $DB_Result2 = new db_query("UPDATE Users_Tbl SET NetWorth = NetWorth + $Value WHERE UserID = $this->UserID");
    return $DB_Result1;
  }

} // End class user

class Car {
  private $CarID, $Owner, $Make, $Model, $Year, $Color, $Value;
  
  __construct ($CarID) {
    $DB_Result = new db_query("SELECT UserID, Make, Model, Year, Color, Value FROM Cars_Tbl WHERE CarID = $CarID");
    $Row = $DB_Result->Fetch();
    $this->CarID = $CarID;
    $this->Owner = $Row['UserID'];
    $this->Make = $Row['Make'];
    $this->Model = $Row['Model'];
    $this->Year = $Row['Year'];
    $this->Color = $Row['Color'];
    $this->Value = $Row['Value'];
  }
  
  function showOwner() {
    return $this->Owner;
  }
  function showDescription() {
    return formatDescription($this->Color, $this->Year, $this->Make, $this->Model);
  }
  function showValue() {
    return $this->Value;
  }
  
  function paint($Color) {
    $this->Color = $Color;
    $DB_Result = new db_query("UPDATE Cars_Tbl SET Color = $Color WHERE CarID = $this->CarID");
    return $DB_Result;
  }
  function reassess($Mileage) {
    $OldValue = $this->Value;
    $this->Value = getBlueBookValue ($this->Make, $this->Model, $this->Year, $Mileage);
    $DB_Result1 = new db_query("UPDATE Cars_Tbl SET Value = $this->Value WHERE CarID = $this->CarID);
    $DB_Result2 = new db_query("UPDATE Users_Tbl SET NetWorth = NetWorth - $OldValue + $this->Value WHERE UserID = $this->UserID");
  }
} // End class car

class boat { // Assume similar to car }
?>

 

And just to be complete, here's how some of the pages that could use those classes would look:

 

<?php UserDetails.php:
include_once("lib/includedClasses.inc");

$ViewedUser = new User($_GET['UserID']);
echo "<h2>$ViewedUser->showFullName()</h2>";
echo $ViewedUser->showAddress()."<br />";
echo "Net Worth: \$".$ViewedUser->showvalue()."<br />";
echo "<br /><b>Cars Owned:</b> <br />";
$DB_Result = new db_query("SELECT Make, Model, Year, Color, Value FROM Cars_Tbl WHERE UserID = ".$ViewedUser->showUser());
while ($Row = $DB_Result->Fetch()) {
  echo sprintf("%s ... Value \$%.2f<br />", showDescription($Row['Color'], $Row['Year'], $Row['Make'], $Row['Model']), $Row['Value']);
}
echo "<br /><b>Boats Owned:</b> <br />";
$DB_Result = new db_query("SELECT Size, Make, Model, Year, Color, Value FROM Boats_Tbl WHERE UserID = ".$ViewedUser->showUser());
while ($Row = $DB_Result->Fetch()) {
  echo sprintf("%s - %s %s %s %s ... Value \$%.2f<br />", $Row['Size'], $Row['Color'], $Row['Year'], $Row['Make'], $Row['Model'], $Row['Value']);
}

?>

 

CarDetails.php:
<?php
include_once("lib/includedClasses.inc");

$ThisCar = new Car($_POST['CarID']);
echo sprintf("This car is a %s, presently valued at \$%.2f.<br />", $ThisCar->showDescription(), $ThisCar->showValue()); 

$ThisOwner = new User($ThisCar->showOwner());
echo "It is owned by " . $ThisOwner->showFullName();
?>

 

PaintCarAndReturn.php:
<?php
include_once("lib/includedClasses.inc");

$ThisCar = new Car($_POST['CarID']);
$ThisCar->paint($_POST['Color']);

header("Location: UserDetails.php?UserID=".$ThisCar->showUser());
?>

 

PaintCarAndShow.php:
<?php
include_once("lib/includedClasses.inc");

$ThisCar = new Car($_POST['CarID']);
$ThisCar->paint($_POST['Color']);

echo "The car now appears ".$ThisCar->showDescription();
?>

 

ReValueCar.php:
<?php
include_once("lib/includedClasses.inc");

$ThisCar = new Car($_POST['CarID']);
$ThisCar->reassess($_POST['Mileage']);

header("Location: UserDetails.php?UserID=".$ThisCar->showUser());
?>

 

OK?  Now I see lots of problems with this class structure

1) Car::reassess() updates users_tbl in away similar to some User class functions but doesn't use the User class.

2) CarDetails.php queries the User and Car tables separately even though it could have been done together.

3) CarDetails.php pulls extra data (Address and NetWorth) as part of the User object even though it isn't used.

4) PaintCarAndShow.php retrieves excess values to create a Car object when Value isn't required and Color will just change anyway.

5) PaintCarAndReturn.php creates an object when none of the retrieved data will be used.

6) ReValueCar.php retrieves excess values when the color isn't required.

 

Leading me to the conclusion that using a class structure is a complete waste and that OOP is not only unnecessary but counter-productive.

 

Alternatively, I could rewrite the pages without using classes at all.  Every time I retrieve only the columns needed with the most efficient query possible.  Like this:

 

CarDetails.php:
<?php
include_once("lib/DB_Handler.inc");
include_once("lib/DisplayFunctions.inc");

$DB_Result = new db_query("SELECT FullName, Color, Year, Make, Model, Value FROM Users_Tbl INNER JOIN Cars_Tbl ON Users_Tbl.UserID = Cars_Tbl.UserID WHERE Cars_Tbl.CarID = ".$_POST['CarID']);
$Row = $DB_Result->Fetch();

echo sprintf("This car is a %s, presently valued at \$%.2f.<br />It is owned by %s.", formatDescription ($Row['Color'], $Row['Year'], $Row['Make'], $Row['Model']), $Row['Value'], $Row['FullName']);
?>

 

And etcetera for the other pages.  There's no question that this non-OOP page executes faster, without excess queries or data retrieval.  It's how I would instintively write things.  But it results in repeated code, function calls with long lists of passed-in variables, and a lack of visible structure.  Which I thought was what OOP is supposed to cure.

 

So I'll re-ask the question: How do people solve this? 

1) Car::reassess() updates users_tbl in away similar to some User class functions but doesn't use the User class.

2) CarDetails.php queries the User and Car tables separately even though it could have been done together.

3) CarDetails.php pulls extra data (Address and NetWorth) as part of the User object even though it isn't used.

4) PaintCarAndShow.php retrieves excess values to create a Car object when Value isn't required and Color will just change anyway.

5) PaintCarAndReturn.php creates an object when none of the retrieved data will be used.

6) ReValueCar.php retrieves excess values when the color isn't required.

 

All your pages are to specialized, they do just one thing (paint, reasses, ..) You even have 2 pages that provide the same functionality PaintCarAndShow, PaintCarAndReturn, possibly due to your J2EE background where these would have been methods with an event listener added to. It would be best to give users a form where they can adjust color, mileage, .. all at once. Which will also decrease the number of pages you currently have and increase the usability of your application.

 

Something I noticed is that your database design structure isn't optimal for example:

 

Asset_Tbl (AssetID, TypeID, UserID, Make, Model, Size, Year, Color, Value)

 

Now the below

 

$DB_Result = new db_query("SELECT Make, Model, Year, Color, Value FROM Cars_Tbl WHERE UserID = ".$ViewedUser->showUser());
while ($Row = $DB_Result->Fetch()) {
  echo sprintf("%s ... Value \$%.2f<br />", showDescription($Row['Color'], $Row['Year'], $Row['Make'], $Row['Model']), $Row['Value']);
}
echo "<br /><b>Boats Owned:</b> <br />";
$DB_Result = new db_query("SELECT Size, Make, Model, Year, Color, Value FROM Boats_Tbl WHERE UserID = ".$ViewedUser->showUser());
while ($Row = $DB_Result->Fetch()) {
  echo sprintf("%s - %s %s %s %s ... Value \$%.2f<br />", $Row['Size'], $Row['Color'], $Row['Year'], $Row['Make'], $Row['Model'], $Row['Value']);
}

 

becomes:

 

$DB_Result = new db_query("SELECT Type, Make, Model, Year, Color, Value FROM Asset_Tbl JOIN Type_Tbl USING TypeID WHERE UserID = ".$ViewedUser->showUser() . " ORDER BY Asset_Tbl.TypeID");

$Type = '';
while ($Row = $DB_Result->Fetch()) {
  if ($Type !== $Row['Type']) {
    $Type = $Row['Type'];
    echo "<h2>$Type</h2>";
  }
  echo sprintf("%s ... Value \$%.2f<br />", showDescription($Row['Color'], $Row['Year'], $Row['Make'], $Row['Model']),
    $Row['Value']);
}
//<h2>Car</h2>
//Car ... Value $
//...
//<h2>Boat</h2>
//Boat ... Value $
//...

 

Your classes are also very tightly coupled to your database normally your Value Object only represents the User it does not load it from the database a DAO is responsible for that. A Value Object can be considered like the Integer class in Java it's only parent is Object, a Value Object is to represent a certain entity throughout your application. Something like:

 

class User {
    private $field1, $field2, $field3, ...;
    //setters,getters (validation, nothing more)
}

 

And used like throughout your application.

 

someFunction(User $u);
someOtherFunction(Integer $i);

 

If however you want to insert/update the user records from the Value Object you should consider using the ActiveRecord which places a save() function in all extending classes which inserts the user if the data is new or updates it (the fields that were changed) when the object is dirty like:

 

$user = new User();
$user->username = 'ignace';
$user->email_address = 'email@domain.com';
$user->save();//INSERT User_Tbl (username, email_address) VALUES (..);
$user->email_address = 'email@domain2.com';
$user->save();//UPDATE User_Tbl SET email_address = .. WHERE UserID = ..;

Your classes are also very tightly coupled to your database normally your Value Object only represents the User it does not load it from the database a DAO is responsible for that. A Value Object can be considered like the Integer class in Java it's only parent is Object, a Value Object is to represent a certain entity throughout your application.

This was the whole question I was trying to ask, thank you.  Don't worry about the DB structure or the page selection, I just made that crap up on the fly purely to ask the coupling question.

 

So how are the VO and the DAO usually tied together?  None of the guides I've seen (and I've searched through a lot) are ever clear on that. 

How should I load data into a VO, especially since sometimes it comes from the DB and sometimes it's user entered (ultimately going to the DB)? 

How is the VO structured if most of the time I'm not retrieving most of the columns?  Isn't it supposed to match the table?

Is the query always page-specific, not object specific?  (And thus not reusable?)

 

Thanks, now we're getting somewhere in that I can at least ask the question better.

 

First I thought you asked about your db structure, then I thought it was about several pages doing one thing, and now your aiming at design patterns? So, what is it your aiming at? domain-modeling?

 

And a VO is a representation of a tuple like for example from a table users (id, username, password, password_salt, email_address)

 

class User {
  private $id, $username, $password, $password_salt, $email_address;
  
  //setters & getters
}

class UserDAO {
  public function findByUsername($username) {/*logic here*/return new User($data); }
}

 

And something else I don't get is:

 

That's reassuring.  It's what I've been doing for 15 years.  Not what I expected though.  I thought the whole J2EE-inspired approach these days was to keep putting in layers of abstraction to ensure reuseability.

 

How come I have to tell you things, you should be telling me? A VO, DAO, ActiveRecord are more then just common in J2EE. They have their own catalogs of design patterns. You could almost say patterns were born in Java.

So, what is it your aiming at? domain-modeling?
I don't know what that term means.  I looked it up and ... maybe?  "Conceptual model of a system".  That sounds too high-level.  "Pattern" was a word you used, I think that sound right.

 

You could almost say patterns were born in Java.
Yes.  And everything I know, I learned before Java.  Before "patterns" was a term.  I didn't even know "pattern" was a term until 2 hours ago.  Therefore I don't know squat about any of it. 

 

All I know is that I've been told proper OOP uses Value Objects now.  So I look up VO and I read they were popularized by J2EE, and thereafter used elsewhere because the structure was deemed superior.  I don't even know what J2EE stands for, though I assumed the J was "Java."  I'm trying to get my head around how people design nowadays (which I have heard called "post-J2EE" approaches, though maybe that was just the one author).  And I can't get a good answer because I see terms and I can look up the term but I don't see how it applies to the whole.

 

This has always been about design patterns, I just didn't know I was supposed to use the word "pattern".  My opening question was how to design a class to incorporate queries and the data that results.  Which I'm still wondering.

 

I keep researching this elsewhere too and something you said enabled me to find something called a "mapper pattern".  It seems like it's getting me closer to what I want.  And yet, I think it has problems.  To clarify the discussion, I'll insert some code.  But this time it's not something I wrote.  This is copied from Advanced PHP Programming by George Schlossnagle, in a section I found explaining the "Mapper" type of "Database Access Pattern".  (He also explains what an "Active Record" pattern is, so I understand what that means now, but it seems even more inferior.)  I'm not offering this code up as a good example or a bad example, just a specific example usable for discussion:

 

<?php
require_once "DB.inc";
class User {
  public $userid;
  public $username;
  public $firstname;
  public $lastname;
  public $salutation;
  public $countryname;

  public function __construct($userid = false, $username = false,
                              $firstname = false, $lastname = false,
                              $salutation = false, $countryname = false)
  {
    $this->userid = $userid;
    $this->username = $username;
    $this->firstname = $firstname;
    $this->lastname = $lastname;
    $this->salutation = $salutation;
    $this->countryname = $countryname;
  }
}

class UserMapper {
  public static function findByUserid($userid)
  {
    $dbh = new DB_Mysql_Test;
    $query = "SELECT * FROM users u, countries c
              WHERE userid = :1
              AND u.countrycode = c.countrycode";
    $data = $dbh->prepare($query)->execute($userid)->fetch_assoc();
    if(!$data) {
      return false;
    }
    return new User($userid, $data['username'], 
                     $data['firstname'], $data['lastname'], 
                     $data['salutation'], $data['name']);
  }

  public static function findByUsername($username)
  {
    $dbh = new DB_Mysql_Test;
    $query = "SELECT * FROM users u, countries c
              WHERE username = :1
              AND u.countrycode = c.countrycode";
    $data = $dbh->prepare($query)->execute($username)->fetch_assoc();
    if(!$data) {
      return false;
    }
    return new User($data['userid'], $data['username'], 
                     $data['firstname'], $data['lastname'], 
                     $data['salutation'], $data['name']);
  }

  public static function insert(User $user)
  {
    if($user->userid) {
      throw new Exception("User object has a userid, can't insert");
    }
    $dbh = new DB_Mysql_Test;
    $cc_query = "SELECT countrycode FROM countries WHERE name = :1";
    list($countrycode) = 
      $dbh->prepare($cc_query)->execute($user->countryname)->fetch_row();
    if(!$countrycode) {
      throw new Exception("Invalid country speicified");
    }
    $query = "INSERT INTO users
                (username, firstname, lastname, salutation, countrycode)
                VALUES(:1, :2, :3, :4, :5)";
    $dbh->prepare($query)->execute($user->username, $user->firstname,
                                   $user->lastname, $user->salutation,
                                   $countrycode);
    list($user->userid) =
      $dbh->prepare("select last_insert_id()")->execute()->fetch_row();
  }

  {
    if(!$user->userid) {
      throw new Exception("User needs userid to call update()");
    }
    $dbh = new DB_Mysql_Test;
    $cc_query = "SELECT countrycode FROM countries WHERE name = :1";
    list($countrycode) = 
      $dbh->prepare($cc_query)->execute($user->countryname)->fetch_row();
    if(!$countrycode) {
      throw new Exception("Invalid country speicified");
    }
    $query = "UPDATE users
              SET username = :1, firstname = :2, lastname = :3,
                  salutation = :4, countrycode = :5
              WHERE userid = :6";
    $dbh->prepare($query)->execute($user->username, $user->firstname,
                                   $user->lastname, $user->salutation,
                                   $countrycode, $user->userid);
  }
  public static function delete(User $user)
  {
    if(!$user->userid) {
      throw new Exception("User object has no userid");
    }
    $query = "DELETE FROM users WHERE userid = :1";
    $dbh = new DB_Mysql_Test;
    $dbh->prepare($query)->execute($userid);
  }
}

$user = UserMapper::findByUsername('george');
print_r($user);
$user->countryname = 'Canada';
UserMapper::update($user);
?>

Now the author doesn't use the term "Value Object", and he doesn't have "getters" and "setters", but I think that's basically what his "User" class is, right?

 

What I like about this code:

1) It allows a User object

2) It separates the queries from the User Object

3) It allows reuse of queries

 

What I don't like about this:

1) It always gets ALL the information for the row

2) It always joins the countries table

3) It updates the entire row for any update

 

Can this "mapper" pattern, or any pattern, or any technique/structure/best practice/approach/some other term I dont' know ... can it keep those good qualities while fixing the bad ones?

The code you provided indeed has some flaws, all fields are for example public which is bad practice as you have no way of applying any validation (and only the User object knows best how it data should look like).

 

Indeed it retrieves the entire row and relevant information from other rows but when you try to abstract you have to retrieve everything as you don't know who will be using it. And to have a great abstraction you need objects to know as little as possible (loose coupling, high cohesion). Like you said "It updates the entire row for any update" is indeed bad and can be easily circumvented by remembering which data has changed, you will however need setters & getters as you can't add code to a class property.

 

Can this "mapper" pattern, or any pattern, or any technique/structure/best practice/approach/some other term I dont' know ... can it keep those good qualities while fixing the bad ones?

 

Dwight D. Eisenhower once said: "I have always found that plans are useless but planning is indispensable". Good quality software starts with planning, more concrete with a domain model (sometimes accompanied by a sequence diagram to make sure your model makes sense). The domain model is a visual representation of your (current) domain knowledge gained through conversation with the domain expert (your client). Because you have a domain model doesn't mean you need to stick to it after all in Agile development is everything optional (except the code). Your domain model is like the "plans" in the Eisenhower quote useless but indispensable to have gone through.

 

Every pattern was/is build on software design principles (and you should know these by heart) and therefor all components have a high separation of concerns (SoC) and a single responsibility (-principle, SRP). Your value object (VO) is so popular due to it solving the problem, have a clear SoC and SRP, while keeping the model simple (and simplicity should always be your aim, our minds have trouble to picture something complex).

 

The mapper you found in Advanced PHP Programming is a rather simple mapper, more effective mappers are for example an object-relational mapper (ORM) which would translate your database (for example by using a database abstraction layer (DBAL, rewrites queries to match the underlying database)) into objects to use in your application.

 

However I probably can keep going on and on and on .. There is nothing that I told you that you can't find in books, if this subject interests you very much I highly recommend: Applying UML and Patterns [Craig Larman], Domain-Driven Design [Eric Evans], Design Patterns [GoF], and Patterns of Enterprise Application Architecture [Martin Fowler]. These last 2 books are catalogs (pattern catalogs) they list the patterns in a common format to explain it's nature, aka's, and suggest usage.

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.