PHP class abstracting/inheritance - php

Probably a silly question, I have many classes in PHP with this template:
class SomeTable {
private $db;
private $prefix;
private $tbl_name;
function __construct($db, $tbl_name) {
$this->db = $db;
$this->prefix = $db->tblPrefix();
$this->tbl_name = $tbl_name;
$this->install();
}
private function install() {
$sql = "CREATE TABLE IF NOT EXISTS...";
$this->db->query($sql, null);
}
// query functions
...
}
I have one of these classes to represent each table in the database.
The 3 private variables at the top must be available in each class,
and they are set in the constructor.
The constructor is the same for every class.
The install() function is different for every class because it is
used to create the database table. (I like to keep it here because
I can keep looking back at it while I'm writing/editing queries).
Do I then create an abstract class with the 3 variables, constructor, empty install() function and implement it in each class? Will it work the same for me?
I'm sorry if it's a dumb question, I've really never used classes/inheritance before in PHP.

Do I then create an abstract class with the 3 variables, constructor,
empty install() function and implement it in each class? Will it work
the same for me?
Yes. The 3 variables will have to be protected now though, instead of private, since they need to be access from child classes. Something like this:
abstract class DBTable{
protected $db;
protected $prefix;
protected $tbl_name;
function __construct($db, $tbl_name) {
$this->db = $db;
$this->prefix = $db->tblPrefix();
$this->tbl_name = $tbl_name;
}
abstract function install();
}
class SomeTable extends DBTable {
private function install() {
$sql = "CREATE TABLE IF NOT EXISTS...";
$this->db->query($sql, null);
}
}

If you extend a class you will inherit any public or protected methods that exist in the parent class. So if you have a class like so:
class Database {
public function get_database(){
return 'blah';
}
}
And then you extend the class like so:
class Blah extends Database {
...
}
Now, the Blah class will inherit the method get_database() so both Database::get_database() and Blah::get_database() will return the same thing. You can also use the parent:: special name to target the extended class. Note: private methods are only available in the class in which they are instantiated and are not inherited.
I should also note that you can override methods by using the same method name in the child class. So if you write a get_database() method within the Blah class, it will take precedence over the parent's method of the same name.

Related

Set a variable of a class in another class

I have a class address_book in which I want to set $DBConnection using method from another class:
<?php
class address_book
{
protected $DBConnection;
public function __construct()
{
$this->DBConnection=new address_book_db();
$this->init();
}
public function init()
{
add_shortcode('address_book', array($this,'load'));
}
public function load()
{
var_dump($DBConnection);
}
}
Another class:
<?php
class address_book_db
{
protected $dbconnection;
public function __construct()
{
$this->dbconnection='1';
}
}
So var_dum should return '1' as it has to be assigned to protected $DBConnection; in the first class. I'm starting my learning of PHP OOP so probably everything is bad.
Any way it has to work like this.
In first class I load db name which is being loaded from another class using methods which determines db name (not developed yet because I just want to pass this constructed dbname to first class).
You're missing $this-> to refer to the class property.
The property's value contains another class, so you have to refer to that class its property as wel with a second ->
var_dump($this->DBConnection->dbconnection)

How to instance a parent class without using DI

I am trying out a MVC concept where the main model for a database stems or trees off other models. I wanted to have a main database sort of model which does the connecting and queries. Then, other models are built to support the controller. Ie, a product model can be extended off the main database model to then query rather than using dependency injection.
My current ideology and attempt looks like this for the main database model:
namespace Portfolio\Application;
abstract class DriverModel {
private static $driver;
private $entity;
private function __construct() {
// Connection to the PDO will be done in here
}
private function __clone() {}
public static function getInstance() {
if(self::$driver)
return self::$driver;
self::$driver = new self();
return self::$driver;
}
protected function q($sql, $values = []) {
$stmt = $this->entity->Prepare($sql);
$stmt->execute($values);
return $stmt;
}
}
Then my example profile controller would look something like this (having the methods to use the main database to run queries):
class ProfileModel extends DriverModel {
public function doSomeQ() {
$this->q('SELECT fname FROM users WHERE id = ?', [(int)1]);
}
}
However, When I execute this line of code:
print_r(ProfileModel::getInstance()->doSomeQ());
I am left with this error: (which makes sense.)
Uncaught Error: Cannot instantiate abstract class.
I then remove the abstract attribute from the class (class DriverModel) but now, the instance I am receiving is the instance from the parent class meaning if I do a print_r() on the getInstance() method to the ProfileModel, there is no doSomeQ() method.
Any help on achieving this methodology would be helpful.
You have to use get_called_class() method instead of new self();
http://php.net/manual/en/function.get-called-class.php
Attached you can find sample who show your expected behaviour
class Main {
public static function getInstance() {
$class = get_called_class();
return new $class();
}
}
class Foo extends Main{
}
var_dump(Foo::getInstance()); // Output Foo object

PHP - Static property in extended/child class

If I have a base abstract class like so:
<?php
abstract class Record {
static $table;
public function getRows () {
return getRowsFromTable(static::$table);
}
}
?>
And I want to extend this class like so:
<?php
class User extends Record {
static $table = 'users';
private $name;
?>
Then if I call:
<?php
$user = new User;
$user->getRows();
?>
Internally, getRows() calls and returns getRowsFromTable('users').
But if I were to create another class that also extends Record:
<?php
class House extends Record {
static $table = 'houses';
private $address;
?>
Then that static $table = 'houses'; declaration overrides the Record::$table and, consequently, breaks the User class.
What's happening is, the declaration static $table = 'houses'; bubbles up to the parent class, so now Record::$table = 'houses';. Since House was declared after User, the next time I call $user->getRows(), internally, User references parent Record and ultimately calls getRowsFromTable('houses') instead of getRowsFromTable('users').
I'm using late static binding so as to get the property from the extended class; but since User and House extend the same parent class, they both end up with the same property value although they override it with different values.
If I were to duplicate the Record class by creating a class Record2 and having House extend Record2, I wouldn't have this problem -- but that wouldn't really help.
Is this the wrong setup? Should I not be using static variables in this context? What should I put in their place, if so? I know that $table doesn't necessarily have to be static, but there are other properties that may need to be static.
I really wouldn't use a static string for this purpose.
How about something like this...
abstract class Record {
/**
* #return table name as string
*/
abstract protected function getTable();
public function getRows () {
return getRowsFromTable($this->getTable());
}
}
Then, your concrete implementations would have to implement getTable, eg
class User extends Record {
protected function getTable() {
return 'users';
}
}

Constructors in Zend model

In my model, I am extending Zend_Db_Table_Abstract. Inside, I have a protected $_name = 'table_name'.
Now, if I define a constructor, and a private variable private $_var and define it inside the constructor, the model does not work anymore! When I call $this->createRow() or anything, nothing happens! Why is this constructor doing this?!
This is what I have:
<?php
class myClass extends Zend_Db_Table_Abstract
{
protected $_name = 'table_name';
private $_var;
public function __construct($var)
{
$this->_var = $var;
}
public function getById($id)
{
$select = $this->select()->where('id =?',$id);
return $this->fetchRow($select);
}
}
This doesn't work! If I remove the __construct(), and the private variable, then it works! Why?
Thanks
If you override the constructor of Zend_Db_Table_Abstract, you should probably call the parent constructor:
class MyClass extends Zend_Db_Table_Abstract
{
protected $_name = 'table_name';
private $_var;
public function __construct($var)
{
$this->_var = $var;
parent::__construct(); // add this
}
// the rest...
}
The parent constructor calls some protected methods _setup() (which in turn calls _setupDatabaseAdapter() and _setupTableName()) and init() (which is empty in the parent, but you can use to add some processing to the final step of object instantiation).
you have to be more clear when asking questions. write code if you have to.. No one can determine whats wrong with your application with only 2 piece of information.. with that said..
first yo should define your variable outside constructor and initialize inside the constructor if you have to.
Your model will not magically know that a db abstract class exist which connects to your database. you will have to create an object inside the model constructor.
here is the code
// DB Abstract class
class Application_Model_DbTable_myTable extends Zend_Db_Table_Abstract
{
protected $_name = 'myTable';
}
// Your model class
class Application_Model_myModel {
private $_var;
public function __construct() {
$this->_var = new Application_Model_DbTable_myTable();
}
}
Hope this was what you were looking for.. and only thing I understood from your question.
dins
It has been implied in other answers but not stated... You class failed because you overrode the constructor and didn't call the parent constructor so the object did not get created properly.
Normally when these classes are constructed, they are constructed through setOptions() which takes an array in constructor. So if you really have to override the constructor you might try something like:
public function __construct($var, $config = array())
{
$this->_var = $var;
parent::__construct($config);
}
now you should be able to set your variable and pass any configuration values you need to pass.
However it would be best to avoid the constructor and use the supplied init() method if at all possible.
<?php
class myClass extends Zend_Db_Table_Abstract
{
protected $_name = 'table_name';
private $_var;
public function init()
{
$this->_var = $var;
}
public function getById($id)
{
$select = $this->select()->where('id =?',$id);
return $this->fetchRow($select);
}
}

PHP Singleton extending class

I am new to OOP (PHP) and just met the design pattern - singleton.
I have found a DB class which uses mysqli (singleton class). I have added some custom methods to it (insert_id(), query(), fetch_result(), etc).
Then I created a new class called UserTools and I want to extend the database class to use the methods I've created previously (query(), fetch_result(), etc).
But I get this error:
Fatal error: Call to private Database::__construct() from invalid context in (...)
when I try to create instance of the new class (User Tools).
What should I do? Is it a right structure?
There are several way to achieve what you want.
One would be :
class UserTools {
private $db;
function __construct() {
$this->db = Database::db_connect();
}
function login() { /* ... */}
}
Although it would be better to directly pass the database instance to the constructor like this :
class UserTools {
private $db;
function __construct($db) {
$this->db = $db;
}
function login() { /* ... */}
}
// Usage
$userTools = new UserTools(Database::db_connect());
If you're really lazy you could just modify your database class and make the constructor public :
class Database {
/* ... */
public function __construct(){/* ... */}
/* ... */
}
class UserTools extends Database {/* ... */}
But I really discourage you to use the latter one. It's really bad code and it doesn't make sense in a logical point of view. Your UserTools class use a database instance. It is not a database.
It is my understanding that only protected and public methods and variables are inherited through extension, not private ones. Try changing your methods/variables from private to protected. public ones are visible to all.
For more information, See: PHP Visibility (Manual)
Edit
Understand the Singleton pattern. It is called 'singleton' because only one instance of a class is expected. Because of this, most classes implementing the singleton pattern define the constructor as private to restrict you from creating more than one.
To create an instance of a singleton, most classes define some kind of getInstance static method, which is public. This public method calls the private constructor, which probably sets flags indiciating that the class has been instantiated in order to prevent further attempts to instantiate the class. The getInstance method returns the results of calling the constructor, essentially the instance of the class.
You could write something like
class UserTools extends DB {
....
}
A quick example on inheritance in PHP:
class A {
public $a;
public function set_a($new_a) { $this->a = $new_a; return $this; }
public function get_a() { return $this->a; }
}
class B extends A {
public $b;
public function set_b($new_b) { $this->b = $new_b; return $this; }
public function get_b() { return $this->b; }
}
$objb = new B();
$objb->set_a("Some Value")->get_a(); //Some Value
The singleton pattern in most cases prevents instantiating the Singleton class by defining the constructor as private (ie private function __construct()).
So if you try to instantiate either your custom class or the original one that you're extending you will get the message above. You should either create a different class or define and use your function as static (eg public static function query($sql, $dbconnection)).
See http://php.net/manual/en/language.oop5.patterns.php

Categories