Extending a class from the class which is extended from mysqli - php

I am not sure if this is possible as I am not very good in OOP programming yet.
I have this db class extended from mysqli,
class database extends mysqli
{
# overwrite parent __construct
public function __construct($hostname = null,$username = null,$password = null,$database = null,$port = null, $socket = null)
{
$hostname = $hostname !== null ? $hostname : ini_get("mysqli.default_host");
$username = $username !== null ? $username : ini_get("mysqli.default_user");
$password = $password !== null ? $password : ini_get("mysqli.default_pw");
$database = $database !== null ? $database : "";
$port = $port !== null ? $port : ini_get("mysqli.default_port");
$socket = $socket !== null ? $socket : ini_get("mysqli.default_socket");
parent::__construct($hostname,$username,$password,$database,$port,$socket);
# check if connect errno is set
if (mysqli_connect_errno())
{
throw new RuntimeException('Cannot access database: ' . mysqli_connect_error());
}
}
# fetches all result rows as an associative array, a numeric array, or both
# mysqli_fetch_all (PHP 5 >= 5.3.0)
public function fetch_all($query)
{
$result = parent::query($query);
if($result)
{
# check if mysqli_fetch_all function exist or not
if(function_exists('mysqli_fetch_all'))
{
# NOTE: this below always gets error on certain live server
# Fatal error: Call to undefined method mysqli_result::fetch_all() in /.../class_database.php on line 28
return $result->fetch_all(MYSQLI_ASSOC);
}
# fall back to use while to loop through the result using fetch_assoc
else
{
while($row = $result->fetch_assoc())
{
$return_this[] = $row;
}
if (isset($return_this))
{
return $return_this;
}
else
{
return false;
}
}
}
else
{
return self::get_error();
}
}
# fetch a result row as an associative array
public function fetch_assoc($query)
{
$result = parent::query($query);
if($result)
{
return $result->fetch_assoc();
}
else
{
# call the get_error function
return self::get_error();
# or:
# return $this->get_error();
}
}
public function query($query)
{
$result = $this->query($query);
if($result)
{
return $result;
}
else
{
return $this->get_error();
}
}
...
# display error
public function get_error()
{
if($this->errno || $this->error)
{
return sprintf("Error (%d): %s",$this->errno,$this->error);
}
}
public function __destruct()
{
parent::close();
//echo "Destructor Called";
}
}
and I have this procedural style of code which I want to turn it into a class that extended from the database class above,
if(isset($_REQUEST['search'])) $search = $_REQUEST['search'];
$sql = "
SELECT *
FROM root_pages
WHERE root_pages.pg_cat_id = '2'
AND root_pages.parent_id != root_pages.pg_id
AND root_pages.pg_hide != '1'
AND root_pages.pg_url != 'cms'
AND root_pages.pg_content_1 LIKE '%".$search."%'
OR root_pages.pg_content_2 LIKE '%".$search."%'
AND root_pages.pg_content_1 NOT LIKE '%http://%'
AND root_pages.pg_content_2 NOT LIKE '%http://%'
ORDER BY root_pages.pg_created DESC
";
$items = $connection->fetch_all($sql);
$total_item = $connection->num_rows($sql);
so I think, by theory I can extend this code into a class like this,
class search extends database
{
public $search = null;
public function __construct($keyword)
{
$this->search = $keyword;
}
public function get_result()
{
$sql = "
SELECT*
FROM root_pages
WHERE root_pages.pg_cat_id = '2'
AND root_pages.parent_id != root_pages.pg_id
AND root_pages.pg_hide != '1'
AND root_pages.pg_url != 'cms'
AND root_pages.pg_content_1 LIKE '%".$this->search."%'
OR root_pages.pg_content_2 LIKE '%".$this->search."%'
AND root_pages.pg_content_1 NOT LIKE '%http://%'
AND root_pages.pg_content_2 NOT LIKE '%http://%'
ORDER BY root_pages.pg_created DESC
";
$items = parent::fetch_all($sql);
return $items;
}
}
then I call the object of search,
$output = new search('1');
print_r($output->get_result());
but I get lots of errors instead,
Warning: mysqli::query()
[mysqli.query]: Couldn't fetch search
in C:\wamp\www\xxx\class_database.php
on line xx
Warning: database::get_error()
[database.get-error]: Couldn't fetch
search in
C:\wamp\www\xxx\class_database.php on
line xx
Warning: mysqli::close()
[mysqli.close]: Couldn't fetch search
in C:\wamp\www\xxx\class_database.php
on line xx
What have I done incorrectly? How can I fix it?
Thanks.
EDIT:
When I tried to call the child class (search) from the parent class (database) in this way,
$database = new database(DB_HOST,DB_USER,DB_PASS,DB_NAME);
print_r(search::get_result());
then I get this error,
Fatal error: Non-static method
mysqli::query() cannot be called
statically in
C:\wamp\www\xxx\class_database.php on
line
Sigh...
Any ideas?

Because the search property in the search class is non-static, you have to first instantiate an object:
$database = new database(DB_HOST,DB_USER,DB_PASS,DB_NAME);
$search = new search("search term");
print_r($search->get_result());
Maybe have a read of a basic tutorial on OOP in PHP, the first one from Google:
http://www.killerphp.com/tutorials/object-oriented-php/
EDIT: There's also a few calls to self::get_error(); in your database class, these should be re-written as instance methods rather than class methods:
$this->get_error()

You are using the wrong method to call methods of the mysqli class in your database class.
You only want to use the double colon :: when accessing static methods, static properties, constants, or the parent of the current method you are overwriting. None of the methods in the mysqli class are static despite how the php manual lists them with the :: in the list of methods.
The way you use parent::__construct() in your __construct() is correct because you are in the current method of the parent you are overwriting.
But in your other methods you want to use $this-> to refer to other parent methods.
In fetch_all() and fetch_assoc() use $this->query() instead of parent::query().
In __destruct() use $this->close(); instead.
When you use self::get_error();, you will either want to change that to $this-> or modify your the function definition to make it static like so public static function get_error()

I think extending the class from database class is probably a bad idea...
Here is my solution:
class search
{
public $mysqli = null;
public function __construct($mysqli)
{
$this->mysqli = $mysqli;
}
public function get_result($parameter)
{
$sql = "
SELECT *
FROM root_contacts_cfm
WHERE root_contacts_cfm.cnt_id = '".$parameter."'
ORDER BY cnt_id DESC
";
$item = $this->mysqli->fetch_assoc($sql);
return $item;
}
}
Passing the object database class into the search class instead, so I can call this search class by this:
$mysqli = new database(DB_HOST,DB_USER,DB_PASS,DB_NAME);
$output = new search($mysqli);
print_r($output->get_result('1'));
Am I right...?

Related

Assign object attributes from the result of prepared statement

I'm wanting to create a new instance of my Class and assign it's attributes the values that are returned. The reason for this is I'm creating a series of methods inheriting from the calling class, as opposed to using static methods which I already had working.
Example of what I'm using currently:
public static function findById($id) {
$id = self::escapeParam($id);
$idVal = is_int($id) ? "i" : "s";
$sql = "SELECT * FROM ".static::$db_table." WHERE id = ? LIMIT 1";
return static::findByQuery($sql,$idVal,$id);
}
public static function findByQuery($sql,$bindChar = '',$bindVal = '') {
try {
$callingClass = get_called_class();
$object = new $callingClass;
$statement = Database::$connection->prepare($sql);
if(!empty($bindChar)) :
$statement->bind_param($bindChar, $bindVal);
endif;
if($statement->execute()) :
$result = $statement->get_result();
$object = $result->fetch_object();
endif;
$statement->close();
if(!empty($object)) :
return $object;
endif;
} catch(Exception $e) {
}
}
What I tried was writing an instantiation method that creates a new instance of my class, and then assign each attribute of the object the value it returns from an array from a tutorial I did. However, the tutorial was fairly outdated and didn't use any new syntax or binding, so I was trying to rework this.
Example from the tutorial below:
public static function find_by_id($id) {
global $database;
$the_result_array = static::find_by_query("SELECT * FROM " . static::$db_table . " WHERE id = $id LIMIT 1");
return !empty($the_result_array) ? array_shift($the_result_array) : false;
}
public static function find_by_query($sql) {
global $database;
$result_set = $database->query($sql);
$the_object_array = array();
while($row = mysqli_fetch_array($result_set)) {
$the_object_array[] = static::instantation($row);
}
return $the_object_array;
}
public static function instantation($the_record){
$calling_class = get_called_class();
$the_object = new $calling_class;
foreach ($the_record as $the_attribute => $value) {
if($the_object->has_the_attribute($the_attribute)) {
$the_object->$the_attribute = $value;
}
}
return $the_object;
}
private function has_the_attribute($the_attribute) {
return property_exists($this, $the_attribute);
}
What I was trying to do from the tutorial, was to return my result as an array using a while, and then assigning a variable by passing the built array into the static::instantation() method, but it doesn't seem to ever be working correctly, as any public functions I create in my calling class (Admin for example) aren't called after as they don't exist due to the Class not being instantiated.
mysqli_result::fetch_object() accepts the class name as the first argument. You can pass the class name as an argument to that method and get the instance of the model. I am not sure why you have that much code but consider my example which I wrote based on your own code:
<?php
class Model
{
public static function findByQuery(string $sql, ?string $bindChar = null, ?string $bindVal = null): ?static
{
$statement = Database::$connection->prepare($sql);
if ($bindChar) :
$statement->bind_param($bindChar, $bindVal);
endif;
$statement->execute();
$result = $statement->get_result();
return $result->fetch_object(static::class);
}
}
class User extends Model
{
private $id;
}
class Database
{
public static mysqli $connection;
}
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
Database::$connection = new mysqli('localhost', 'user', 'password', 'test');
$user = User::findByQuery('SELECT ? as id', 's', 'Dharman');
var_dump($user);
The output from that example is:
object(User)#4 (1) {
["id":"User":private]=>
string(7) "Dharman"
}
As you can see, the code created an instance of the class using late-static binding and it also assigned the value to a private property, which you can't do otherwise.
P.S. My example is a little bit tidier. I added parameter typing and removed a lot of unnecessary code. In particular, I remove empty try-catch which is a terrible practice.
I have now got this working, although I feel this is probably not the best way of doing it.
I'm primarily front end so please comment if there are improvements or best practices.
public static function findByQuery($sql,$bindChar = '',$bindVal = '') {
try {
$statement = Database::$connection->prepare($sql);
if(!empty($bindChar)) :
$statement->bind_param("$bindChar", $bindVal);
endif;
if($statement->execute()) :
$result = $statement->get_result();
$output = $result->fetch_object();
endif;
$statement->close();
if(!empty($output)) :
$class = get_called_class();
$object = new $class;
foreach(get_object_vars($output) as $key => $value) :
$object->$key = $value;
endforeach;
endif;
if(!empty($object)) :
return $object;
endif;
} catch(Exception $e) {
}
}
My initial thoughts were declaring an object and then I thought that the PHP fetch_object call would have just assigned my object it's properties after initiating the Class but that wasn't the case.
So what I've done is that if the statement is successful and a results object is created, I then get the object properties and values with the get_object_vars() command, and then loop through these as a key value pair, assigning each attribute it's returned value.
I can confirm this works as I can now run $admin->remove() from my removal script, as opposed to what I was having to do before which was Admin::remove($id);

Accessing class properties in PHP OOP

I am new in PHP OOP and was wondering if someone could help me with this.
I have a basic class with one method which returns data from database. Currently I am calling the method which displays everything inside the function.
Here is my class Definition:
class Products{
//properties
public $familyName = "";
public $familyProduct = "";
//Methods
public function getFamily($catId){
global $conn;
$sql = "SELECT * FROM product_family WHERE catID = '$catId'";
$result = $conn->query($sql);
if($result->num_rows > 0){
while($row = $result->fetch_assoc()){
echo "<li>".$row['familyName']."</li>";
echo "<li>".$row['familyProduct']."</li>";
}
}
}
}
Here is how I call the method:
$Products = new Products;
$Products->getFamily( 4 );
This works however, how can I assign each data coming from database ( ex familyName, familyProduct ) into variables inside class implementation and then access them individually where ever I need to. Something like this:
$Products = new Products;
$Products->familyName;
$Products->familyProduct;
I have empty properties but I am not sure how can I assign values to them coming from the loop and then return them each.
Thanks,
There are view things I would change in your Code.
Don't make Properties public use use Getters and Setters.
This will protect you Object from being used the wrong way e.g. now you can't change the familyName from outside: $products->familyName = "some value" because this would make the data of the object corrupt.
global $conn; is a no go in OOP use the construct of the Object,
in your case $products = new Products($conn);
Now you can set a Cat ID $products->setCatId(4); and read the result
$familyName = $products->getFamilyName(); or $familyProduct = $products->getFamilyProduct();
If you have more than one result you will get an array, if catId will always result one row you can delete this part. If you learn more about OOP you will find out that the hole SQL stuff can be done with a separate Object, but this is off Topic.
class Products
{
// Properties
protected $conn;
protected $catId;
protected $familyName;
protected $familyProduct;
public function __construct($conn)
{
$this->conn = $conn;
}
// set Cat ID and get date
public function setCatId($catId)
{
$this->catId = (int) $catId;
$this->getDate();
}
public function getCatId()
{
return $this->catId;
}
// get Family Name
public function getFamilyName()
{
return $this->familyName;
}
// get Family Product
public function getFamilyProduct()
{
return $this->familyProduct;
}
// get date
protected function getDate()
{
$sql = "SELECT * FROM product_family WHERE catID = '$this->catId'";
$result = $this->conn->query($sql);
// Default if no result
$this->familyName = null;
$this->familyProduct = null;
// if one Result
if ($result->num_rows == 1)
{
$row = $result->fetch_assoc();
$this->familyName = $row['familyName'];
$this->familyProduct = $row['familyProduct'];
}
if ($result->num_rows > 1)
{
$this->familyName = [];
$this->familyProduct = [];
while ($row = $result->fetch_assoc())
{
$this->familyName[] = $row['familyName'];
$this->familyProduct[] = $row['familyProduct'];
}
}
}
}

Parent's class constructor call error?

I'm trying to call parent class constructor but it throws an error
Fatal error: Cannot call constructor
and the same code was working well before, I didn't change anything and but suddenly don't know what could have happened, It is throwing this error.
I've read some answers on stackoverflow, but they say that your parent class doesn't contain a constructor, well, this is not my case I have constructor in my parent class. Here's my code:
class DB
{
var $con;
public function __construct()
{
require_once 'configs/dbconfig.php';
$this->connect();
}
function connect()
{
try{
$this->con = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME,DB_USER,DB_PASS);
$this->con->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
}catch(PDOException $ex) {
echo $ex->getMessage();
}
}
}
and then I have a class Posts which is extending DB and calling DB's constructor.
class Posts extends DB
{
var $user_id;
public function __construct($user_id)
{
parent::__construct();
$this->user_id = $user_id;
}
function get_posts($search,$pages,$since_id=0,$max_id=false,$count=20,$status='active')
{
$extra = '';
if($search) $extra .= " AND text LIKE CONCAT('%',:search,'%')";
if(!empty($pages)) $extra .= " AND page IN('".implode("','", $pages)."')";
if(!empty($status) && $status != 'all') $extra .= " AND status=:status";
$max_id = ($max_id) ? $max_id : time();
$sqlCommand = "SELECT id,pid,text,media,media_url,type,name,u_id,username,user_profile_url,user_photo_url,post_url,date,status,source,page_id FROM ".POSTS." WHERE date>=:since_id AND date<=:max_id".$extra." AND user_id=:user_id ORDER BY date DESC LIMIT $count";
$params = array(':since_id'=>$since_id,':max_id'=>$max_id,':user_id'=>$this->user_id);
if($search) $params[':search'] = $search;
if($status && $status != 'all') $params[':status'] = $status;
$posts = $this->fetch($sqlCommand,$params);
return $posts;
}
}
This is with your php version, It must have been changed, please check the version of php and try to update that.
I think this is a known PHP bug (look here), try to make a function in DB that returns a new DB (by calling constructor) and in Posts call this function that you created.
Well maybe it's a basic silly answer, but did you try 'require'?

calling statically and dynamically same method

i'm writing a php class that is like an orm.
I have a method, that can be called statically or instanciated, and it must work in both cases.
Can you see what's wrong.
Basically is an object called Model.
When created it creates a table based on the inherited class.
For example:
Podcast extends Model ....
There are some functions like this that needs to be called statically and dynamically.
for example:
$podcastList = Podcast::findAll($db);
I get all podcasts objects from DB without need to have a podcast object instanciated.
But i can also do:
$podcast = new Podcast($db)
$podcastList = $podcast->findAll(); //no db here.... passed before
$db is a class i wrote to make operation on Database. IT simply does with OOP, what mysql_* do with functions. I'm not using PDO, i may use in future, but now i use mysql_* :P
that are the incriminated functions
public static function findAll($db=NULL, $self=NULL) {
if($self == NULL) {
$self = new static($db);
} else {
$self = $this;
}
$self->tableName = "";
$self->db = NULL;
$is_static = !(isset($this) && get_class($this) == __CLASS__);
if($is_static) {
//die(__CLASS__ . "::" . __FUNCTION__ . " CALLED STATICALLY");
if(!$self->db) {
die(__CLASS__ . "::" . __FUNCTION__ . " CALLED STATICALLY AND DB IS NULL");
//It stops here!
}
$self->tableName = $self->genTableName();
} else {
$self->db = $this->db;
$self->tableName = $this->tableName;
}
$query = "SELECT * FROM {$self->tableName}";
$r = $self->db->exec($query);
if(!$r) {
die(__CLASS__ . ":Error " . __FUNCTION__ . " record: " . $self->db->getError());
}
if($self->db->countRows($r) == 0) {
return NULL;
}
$objects = array();
while($row = $self->db->fetch($r, DBF::FETCH_ASSOC)) {
$objectClass = __CLASS__;
$object = new $objectClass($this->db);
//TODO Do it dinamically indipendently of column name
$f = get_class_vars($objectClass);
foreach ($f as $field => $value) {
$chuncks = explode("_", $field);
if($chuncks[0] == "f") {
$object->{$field} = $row[$chuncks[2]];
}
}
$objects[] = $object;
}
return $objects;
}
public function __call($name, $arguments) {
if ($name === 'findAll'){
return static::findAll($arguments, $this);
}
}
Both are part of a class.
Thank you for the help !
There's a lot wrong with this code. More important than your many logic mistakes (why are you setting $self = $this, then $self->db = NULL, then $self->db = $this->db?) is that you are misunderstanding what it means to be able to call static functions dynamically in PHP. The object $this simply doesn't exist in a static method. The call $podcast->findAll() looks non-static, but it's still static.
To do what you want to do, here are some options:
leave the function static and call findAll($this->db, $tablename) as needed
put the function into the db class and call it with parameter tablename
EDIT:
The second in my list is how I would do it. This is because you already have to have a db object in your original example, and there is nothing in particular that makes the function's purpose only suited to Podcast objects and not to, say, any other object representing database rows.
//calling examples:
$podcastlist = $db->findAll('Podcast');
$podcast = new Podcast($db);
$podcastlist = $podcast->findAll();
public class db {
....
function findAll($classname, $tablename=NULL) {
if(!isset($tablename)) {
//let's pretend you put default table names as class constants
$tablename = get_constant($classname.'::DEFAULT_TABLE');
}
$query = "SELECT * FROM {$tableName}";
$r = $this->exec($query);
if(!$r) {
throw new Exception("Error " . __FUNCTION__ . " record: " . $this->getError());
}
if($this->countRows($r) == 0) {
return NULL;
}
$objects = array();
while($row = $this->fetch($r, DBF::FETCH_ASSOC)) {
$object = new $classname($this);
//the following is an easier way to do your original foreach
foreach($row as $field=>$value) {
if(property_exists($classname, "f_".$field)) {
$object->{'f_'.$field} = $value;
}
}
$objects[] = $object;
}
//something you forgot:
return $objects;
}
}
public class Podcast extends Model {
....
public function findAll($tablename=NULL) {
return $this->db->findAll(class_name($this), $tablename);
}
}

php function not returning class object

I have a DB class that I've created several functions in to return various values. One of the functions returns (or is supposed to) a "user" class object that represents a logged in user for the application.
class user {
public $guid = '';
public $fname = '';
public $lname = '';
public function __construct() {}
public function print_option() {
return "<option value='$this->guid'>$this->name</option>";
}
}
In the DB class I have the following 2 functions:
public function get_user($uid) {
$sql = '';
$usr = new user();
$sql = "SELECT guid, fname, lname FROM ms.users WHERE guid=?";
if($sth = $this->conn->prepare($sql)) {
$sth->bind_param('s', $uid);
if($sth->execute()) {
$sth->bind_result($usr->guid, $usr->fname, $usr->lname);
$sth->fetch();
print_r($usr); // PRINTS OUT CORRECTLY
return $usr;
}
else {return null;}
}
else {return null;}
}
public function get_practice_user_list($pguid) {
$ret = '';
$sql = "SELECT user_guid FROM ms.perm WHERE p_guid=?";
if($sth = $this->conn->prepare($sql)) {
$sth->bind_param('s', $pguid);
if($sth->execute()) {
$usr = new user();
$guid = '';
$sth->bind_result($guid);
while($sth->fetch()) {
print_r($guid); // PRINTS GUID correctly
$usr = $this->get_user($guid);
print_r($usr); // PRINTS NOTHING object is null so prints "error" two lines later.
if($usr != null) $ret .= $usr->print_option();
else print "error";
}
return $ret;
}
else {return null;}
}
else {return null;}
}
I'm just not understanding why the "user" object is not returning in this instance. Others calls to the get_user function work just fine and return the user class object pertaining to that user.
TIA
I guess you guid may be an integer so
$sth->bind_param('s', $uid);
bind_param's first param should be 'i' not 's';
http://www.php.net/manual/en/mysqli-stmt.bind-param.php
The problem was with the query. Since the code was just looping through one query (get_practice_user_list), then calling the get_user function and attempting a second query MySQL came back with an error of out of sync message. When I looked that up, I was able to fix it by doing a fetch_all on the first query then looping through that array to get the users.

Categories