I come from the world of procedural programming with assembler being my first language and PL/1 and Cobol being the languages I learnt most of my (bad) habits from.
This is my first post here, so please accept my apologies if I am not doing it 100% correct.
I am rewriting our frontend and backend systems from procedural php to OOP and I don't really understand what structure to use.
In the old system, we have a number of scripts that include xxx.inc.php with the functions used and those xxx.inc.php in turn includes other xxx.inc.php such as db.inc.php, api_pj.inc.php etc
Trying to do this OOP, I create one file for each class and autoloads them, but I can't understand how to handle the common classes (database, connection to external api's, etc). When testing, I used inheritance and it works fine, but it feels very odd. I can't really see customer class being a child of database class. Also I do not understand what needs to be defined and not. Should all variables in the classes be defined?
The example below do not work, since the database connection is not available for the Customer class. When having "class Customer extends DB" instead it all works fine, but I suppose there is a more correct way of doing this?
Edited code below after reply from Alex Andrei
the_example_page.php
// autoloader
spl_autoload_register(function ($class) {
require_once 'classes/' . $class . '.class.php';
});
// data for testing
$customer_id = '12090';
$order_id = '31480';
// db
$db = new DB();
$db_conn = $db->db_connect();
// get customer name data and print it:
$customer = new Customer($db_conn);
$customer_data = $customer->get_customer_info($customer_id);
print $customer_data['name'];
// get order date and print it:
$order = new Order($db_conn);
$order_data = $order->get_order_info($order_id);
print $order_data['date'];
DB.class.php
class DB
{
public function __construct() {}
public function db_connect()
{
static $db_conn;
if( !$db_conn )
{
$db_conn = pg_connect("dbname=database user=user password=PaSsWoRd");
}
return $db_conn;
}
public function db_exec($sql)
{
if( !$sql ) return;
$db_conn = $this->db_connect();
$result = @pg_exec($db_conn,$sql);
if( !$result )
{
return;
}
$ret[result] = $result;
return $ret;
}
public function db_getrow(&$a)
{
# a bunch of stuff in real function, but here only a plain fetch_array as example
$ret = pg_fetch_array($a);
return $ret;
}
}
Customer.class.php
class Customer
private $conn;
{
public function __construct($db)
{
$this->conn=$db;
}
public function get_customer_info($customer_id)
{
return $this->conn->db_getrow($this->conn->db_exec("SELECT * FROM customer WHERE customerid = $customer_id;"));
}
public function get_all_customers($status)
{
return $this->conn->db_exec("SELECT * FROM customer WHERE status = $status;");
}
}
Order.class.php
class Order
{
private $conn;
{
public function __construct($db)
{
$this->conn=$db;
}
public function get_order_info($order_id)
{
return $this->conn->db_getrow($this->conn->db_exec("SELECT * FROM order WHERE orderid = $order_id;"));
}
public function get_all_orders($status)
{
return $this->conn->db_exec("SELECT * FROM order WHERE status = $status;");
}
}
UPDATE
Extended example on how to modify the DB connection class and the Order class
database class
class DB
{
private $conn;
private $user;
private $password;
private $database;
public function __construct($database,$username,$password){
$this->user = $user;
$this->password = $password;
$this->database = $database;
}
public function connect(){
if( !$this->conn ){
$this->conn = pg_connect("dbname=$this->database user=$this->user password=$this->password");
}
return $this->conn;
}
public function db_exec($sql){
if( !$sql ) return; // add some relevant error message
if (!$this->conn){
$this->connect();
}
$result = pg_exec($this->conn,$sql);
if( !$result ){
return;
}
$ret[result] = $result;
return $ret;
}
public function db_getrow(&$a){
# a bunch of stuff in real function, but here only a plain fetch_array as example
$ret = pg_fetch_array($a);
return $ret;
}
}
Order class
class Order
{
private $dbObj;
public function __construct($db)
{
$this->dbObj=$db;
}
public function get_order_info($order_id)
{
$result = $this->dbObj->db_exec("SELECT * FROM order WHERE orderid = $order_id;");
return $this->dbObj->db_getrow($result);
}
public function get_all_orders($status)
{
return $this->conn->db_exec("SELECT * FROM order WHERE status = $status;");
}
}
Usage
$db = new DB("myDatabase","user","password");
$db->connect();
$order = new Order($db);
You don't have to extend
everything, only what makes sense.
Just pass the db connection as a parameter to the other objects, so the queries can be executed.
Ex.
$customer = new Customer($db);
$order = new Order($db);
You should add a private variable for each class to hold the database connection, ex
class Customer{
private $conn;
// ...
}
and the constructor would look like this
public function __construct($db) {
$this->conn = $db;
}
using it in the methods like so
public function get_order_info($order_id){
return $this->conn->db_getrow($this->db_exec("SELECT * FROM order WHERE orderid = $order_id;"));
}
You can extend
the other classes if they use the database
class and then invoke them in the child constructor. Watch out for conflicting variables within the methods if you choose this approach.
If the Customer
class extends the Database
class then it will have access to the methods of Database
etc
class Database{
public function __construct(){
}
/* other methods*/
}
class Customer extends Database{
public function __construct(){
parent::__construct();
}
}
class Order extends Database{
public function __construct(){
parent::__construct();
}
}