I made a db connection class like this :
class db_connector{
const host = "localhost";
const user = "root";
const password = "";
const dbname = "test";
public $db;
public function __construct()
{
$database = $this::dbname;
$db = new mysqli($this::host, $this::user, $this::password, $this::dbname);
if($db->connect_errno)
{
die (mysql_connect_error());
}
return $db;
}
}
When i create an object for the class it works fine enough, though then i want to make a query within the class i get the following error:
Fatal error: Call to undefined method db_connector::query()
The object is as follows (outside the class):
$con = new db_connector;
$con->query("SELECT * FROM test");
The connection gets established, just the query gives the error. I thought the object would inherit the mysqli methods since I returned it. Can anybody help me fix this one? Im fairly new in OOP so maybe my logic is not the best.
How about a little magic:
class db_connector{
protected $connectionData = array(
'host' => 'localhost',
'user' => 'root',
'password' => '',
'dbname' => 'test',
);
protected $db;
public function __construct($connectionData=array())
{
$cd = array_merge($this->connectionData, $connectionData);
$this->db = new mysqli($cd['host'], $cd['user'], $cd['password'], $cd['dbname']);
if($this->db->connect_errno)
{
die (mysql_connect_error());
}
}
public function foo()
{
return 'I am foo, i exist so i will be called even if mysqli::foo would exist, because i am defined in this class!';
}
public function __get($property)
{
if(property_exists($this->db, $property)){
return $this->db->$property;
}
}
public function __call($name, $args)
{
if(method_exists($this->db, $name))
return call_user_func_array(array($this->db, $name), $args);
}
}
And call it like:
$db = new db_connector();
$result = $db->query('SELECT foo FROM bar LIMIT 1');
// this should also work:
echo $result->num_rows;
The class constructor won't return the mysqli object as you expect.
$con = new db_connector;
//$con is a db_connector object not a mysqli object
$con->query("SELECT * FROM test");
Try:
$con = new db_connector;
$con->db->query("SELECT * FROM test");
instead. $con->db is a public field which holds the mysqli object.
Edit
Also change:
$db = new mysqli($this::host, $this::user, $this::password, $this::dbname);
if($db->connect_errno)
{
die (mysql_connect_error());
}
return $db;
To:
$this->db = new mysqli($this::host, $this::user, $this::password, $this::dbname);
if($this->db->connect_errno)
{
die (mysql_connect_error());
}
Related
I recently started to resurrect an old PHP4.# website. In doing so I've been trying to figure out the conversion to PDO. After so many different attempts at PDO structure I'm getting lost and my code keeps getting more and more wild. I believe the problem is that I'm not correctly instantiating the pdo class but have failed using several different methods.
Fatal error: Uncaught Error: Call to undefined method DBInsert::__construct() in /home/folder/database/insert.php:36
mysqlhelper.php
<?php
class MySQLHelper
{
private $link;
private $db;
private $user;
private $pass;
var $pdo;
public function __construct()
{
$host = 'localhost';
$db = 'dbname';
$user = 'username';
$pass = 'supersecret';
$port = '1111';
$options = [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
\PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$dsn = "mysql:host=$host;dbname=$db;port=$port";
$this->pdo = new PDO($dsn, $user, $pass, $options);
return $this->pdo;
} catch (\PDOException $e) {
throw new \PDOException($e->getMessage(), (int)$e->getCode());
echo "Connection failed";
}
}
function __destruct()
{
// Closing connection
$this->pdo = null;
}
public function query($query)
{
// Performing SQL query
$result = $this->pdo->query($query) or die('Query failed: ' . mysql_error());
return $result;
}
insert.php
<?php
include "../helpers/mysqlhelper.php";
class DBInsert
{
private $helper;
private $nations;
private $nations_updated;
private $stagedslots;
private $stagednations_new; // nations that need to be inserted
private $stagednations_old; // nations that need to be updated
var $pdo;
//this function was previously called __construct() prior to my 11/22/20 updates
function somethingelse()
{
$this->helper = new MySQLHelper();
$this->stagedslots = array();
$this->nations_updated = array();
$this->nations = array();
$q = "SELECT * FROM `nations`";
$result = $this->helper->query($q);
$result = $this->helper->getResultArray($result);
foreach ($result as $val)
{
$this->nations[$val["id"]] = $val;
}
}
public function clearSlots()
{
$sql = "DELETE FROM `slots`;";
$object = new MySQLHelper;
$object->__construct();
line 36 $stmt = $this->__construct()->prepare($sql);
$stmt->execute();
}
For line 36 I've also tried $stmt = $this->pdo->prepare($sql); which yields errors also.
Appreciate any suggestions.
$object = new MySQLHelper;
$object->__construct();
$stmt = $this->__construct()->prepare($sql);
You are writing the same thing three times here:
You make a new object $object and then you do not need to call __construct because by making the object it automatically calls the constructor already.
$sql = "DELETE FROM `slots`;";
$object = new MySQLHelper; //automaticcally calls the constructor
$stmt = $object->prepare($sql);
$stmt->execute();
$stmt->close(); // close connection.
READ THIS
I'm trying to learn OOP programming in PHP. So far i got couple of basic things but i don't know how to echo my results into the page. I made up Database class and Category class i wanna echo the results from read function but i don't know how to do that. Can someone help me refactor this so i can use both classes?
This is my code
class Database {
private $_connection;
private $_host = "localhost";
private $_username = "root";
private $_password = "";
private $_database = "cmsi";
// Conect to database is private and can only be used by getConection function result is returned object of mysqli class
//can be seen in var_dump() function
private function conect() {
//$this refers to class Database and her functions and propertys are being acsessed via -> sign
$this->_connection = new mysqli($this->_host, $this->_username,
$this->_password, $this->_database);
// Error handling
if(mysqli_connect_error()) {
trigger_error("Failed to conencto to MySQL: " . mysql_connect_error(),
E_USER_ERROR);
}
return $this->_connection;
}
//Safest way to iniate conection
public function getConection(){
return $this -> conect();
}
}
class Category{
private $db_sql;
public function __construct($db){
$this->db_sql = $db;
}
public function read(){
$conection = $this->db_sql;
$query = "Select * from category";
$result = $conection -> query($query);
return $result;
}
}
$db = new Database();
$conection = $db->getConection();
$obj = new Category($conection);
$obj->read();
You're almost there. Just store the result set in a variable and loop through it, like this:
// your code
$db = new Database();
$conection = $db->getConection();
$obj = new Category($conection);
// store the result set
$result_set = $obj->read();
// loop through the result set
while($row = $result_set->fetch_array()){
// your code
}
here's what I have:
class Entry
{
public $id;
public $name;
public $seoName;
public $timeCreated;
public function someFunction()
{
}
}
class EntryMapper
{
protected $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
public function saveEntry(Entry &$entry)
{
if($entry->id){
$sql = "";
}
else {
$sql = "INSERT INTO tbl_entry (name, seo_name, time_created) VALUES (:name, :seo_name, :time_created)";
$stmt = $this->db->prepare($sql);
$stmt->bindParam("name", $entry->name);
$stmt->bindParam("seo_name", $entry->seoName);
$stmt->bindParam("time_created", $entry->timeCreated);
$stmt->execute();
$entry->id = $this->db->lastInsertId();
}
}
}
Now, here's how I use it in my view file (currently just testing insert command):
$entry = new Entry();
$entry->name = "Some Company LLC";
$entry->seoName = "some-company-llc";
$entry->timeCreated = date("Y-m-d H:i:s");
$entryMapper = new EntryMapper(new PDO("mysql:host=....."));
$entryMapper->saveEntry($entry);
I want to have the $entryMapper line like this:
$entryMapper = new EntryMapper(new Database());
meaning I should have a separate class Database.php where I would establish the connection.
I tried that, but since my class EntryMapper.php needs an instance of PDO directly, i'm getting an error. I have tried Database extend from PDO but that also raises error saying that PDO constructor was not called in EntryMapper
Any thoughts?
EDIT: if you see any signs of code coupling or similar, let me know because I want to learn to code properly. Thank you very much!
You can use Factory pattern and create the PDO object within a function in the Database class.
class Database {
private const connStr = 'mysql:host=.....';
public static function createPDODatabase() {
return new PDO(connStr);
}
}
So you may call your EntryMapper constructor as:
$entryMapper = new EntryMapper(Database::createPDODatabase());
EDIT: If you want to do it by instantiating the Database object, you should call the PDO constructor in the constructor of the Database class.
class Database extends PDO {
public function __construct($dbname='db_name', $server='localhost', $username='db_user', $password='db_password') {
parent::__construct("mysql:host=$server;dbname=$dbname", $username, $password);
parent::setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
}
}
Then you may just instantiate the Database object.
$entryMapper = new EntryMapper(new Database());
This is how I finally solved it (if a better implementation arises, I will for sure recode). It is an implementation of solution under the accepted answer here: Global or Singleton for database connection?
My ConnFactory.php
include('config/config.php');
class ConnFactory
{
private static $factory;
public static function getFactory()
{
if(!self::$factory){
self::$factory = new ConnFactory();
return self::$factory;
}
}
private $db;
public function pdo()
{
if(!$this->db){
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"
);
$this->db = new PDO("mysql:host=".DB_HOST.";port=".DB_PORT.";dbname=".DB_SCHEMA."", DB_USER, DB_PASS, $options);
}
return $this->db;
}
}
Usage in my view/html file (just a test of insert functionalty):
$entry = new Entry();
$entry->name = "Kartonaža ad Gradačac";
$entry->seoName = "kartonaza-ad-gradacac";
$entry->timeCreated = date("Y-m-d H:i:s");
$entryMapper = new EntryMapper(ConnFactory::getFactory()->pdo());
$entryMapper->saveEntry($entry);
I now learn PHP OOP and I want to get yours tips. I create DB connection class, is this okay?
How use "Database" class in another class? Always use "extends"?
Thanks
<?php
//Config
$db_user = 'root';
$db_pass = '';
$db_host = 'localhost';
$db_name = 'test';
class Database
{
private $Database;
private static $instance;
public static function instance()
{
if ( !self::$instance )
self::$instance = new Database();
return self::$instance;
}
public function connect($host, $user, $password, $name)
{
$this->db_link = mysql_connect($host, $user, $password);
mysql_set_charset('utf8');
mysql_select_db($name, $this->db_link);
}
public function query($query)
{
$sql = mysql_query($query);
$row = mysql_fetch_array($sql);
return $row;
}
}
class Book extends Database
{
public function getData2()
{
$sql = $this->query('SELECT * FROM users WHERE price = "7"');
return $sql['name'];
}
}
$db = Database::instance();
$db->connect($db_host, $db_user, $db_pass, $db_name);
$b = new Book();
$res = $b->getData2();
print_r($res);
?>
You can extends the database class but it is not what I suggest.
You could also use the keyword global inside the function where you actually need database to let the function get the database instance from outside; but still some people get nervous about global keyword.
You could pass the instance of the database class as argument of constructor of the class where you need database, in this way you will be able to call db methods with a simple chain: $this->db->connect();
As björn states, a book is not a database, wouldn't recommend extend.
One approach would be to on object initialization of book, pass the reference to the db-layer/object..
$myDb = new dbLayer($settings);
$myBook = new book($myDb);
$a = $myBook->getAllData($someParameter);
in the constructor of book you save the reference to the dblayer...
class book {
var $dbTier;
__constructor($db) {
$this->dbTier = $db;
...
regards,
//t
You can do something like this, this should be easy to understand
class User{
private $db;
public function __construct($db){
$this->db = $db;
}
}
The $db in the constructor parameter would be your database connection and required fom another file. All any how you get to it but not in the class.
require 'file from where you have your database';
$user = new User($db);
Anytime you like to user the database to make a query, you just reference it like below
$this->db->query();
You would not use extends here ideally unless Book is a subclass of Database. You would do something like:
class Book
{
var $db;
function __construct() {
$this->db = Database::instance();
}
}
Then the book class would use the instance of the database object and you can always access it with $this->db inside Book.
I want to be able to make classes which extend the MySQLi class to perform all its SQL queries.
$mysql = new mysqli('localhost', 'root', 'password', 'database') or die('error connecting to the database');
I dont know how to do this without globalising the $mysql object to use in my other methods or classes.
class Blog {
public function comment() {
global $mysql;
//rest here
}
}
Any help would be appreciated.
Thanks.
I was working on something similar. I'm happy about this singleton class that encapsulates the database login.
<?php
class db extends mysqli
{
protected static $instance;
protected static $options = array();
private function __construct() {
$o = self::$options;
// turn of error reporting
mysqli_report(MYSQLI_REPORT_OFF);
// connect to database
#parent::__construct(isset($o['host']) ? $o['host'] : 'localhost',
isset($o['user']) ? $o['user'] : 'root',
isset($o['pass']) ? $o['pass'] : '',
isset($o['dbname']) ? $o['dbname'] : 'world',
isset($o['port']) ? $o['port'] : 3306,
isset($o['sock']) ? $o['sock'] : false );
// check if a connection established
if( mysqli_connect_errno() ) {
throw new exception(mysqli_connect_error(), mysqli_connect_errno());
}
}
public static function getInstance() {
if( !self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
public static function setOptions( array $opt ) {
self::$options = array_merge(self::$options, $opt);
}
public function query($query) {
if( !$this->real_query($query) ) {
throw new exception( $this->error, $this->errno );
}
$result = new mysqli_result($this);
return $result;
}
public function prepare($query) {
$stmt = new mysqli_stmt($this, $query);
return $stmt;
}
}
To use you can have something like this:
<?php
require "db.class.php";
$sql = db::getInstance();
$result = $sql->query("select * from city");
/* Fetch the results of the query */
while( $row = $result->fetch_assoc() ){
printf("%s (%s)\n", $row['Name'], $row['Population']);
}
?>
My suggestion is to create a Singleton DataAccess class, instantiate that class in a global config file and call it in your Blog class like $query = DataAccess::query("SELECT * FROM blog WHERE id = ".$id).
Look into the Singleton pattern, it's a pretty easy to understand designpattern. Perfect for this situation.
Your DataAccess class can have several methods like query, fetchAssoc, numRows, checkUniqueValue, transactionStart, transactionCommit, transactionRollback etc etc. Those function could also be setup as an Interface which gets implemented by the DataAccess class. That way you can easily extend your DataAccess class for multiple database management systems.
The above pretty much describes my DataAccess model.
You can use PHP's extends keyword just for any other class:
class MyCustomSql extends mysqli {
public function __construct($host, $user, $password, $database) {
parent::__construct($host, $user, $password, $database);
}
public function someOtherMethod() {
}
}
$sql = new MyCustomSql('localhost', 'root', 'password', 'database') or die('Cannot connect!');
or better use object aggregation instead of inheritance:
class MySqlManipulator {
private $db;
public function __construct($host, $user, $password, $database) {
$this->db = new mysqli($host, $user, $password, $database);
}
public function someOtherMethod() {
return $this->db->query("SELECT * FROM blah_blah");
}
}
$mysqlmanipulator = new MySqlManipulator('localhost', 'root', 'password', 'database') or die('Cannot connect!');
My standard method is to make a singleton class that acts as the database accessor, and a base class that everything requiring such access inherits from.
So:
class Base {
protected $db;
function __construct(){
$this->db= MyDBSingleton::get_link();
//any other "global" vars you might want
}
}
class myClass extends Base {
function __construct($var) {
parent::__construct();// runs Base constructor
$this->init($var);
}
function init($id) {
$id=(int) $id;
$this->db->query("SELECT * FROM mytable WHERE id=$id");
//etc.
}
}
Have a look at PDO, which throw exceptions for you to catch if a query fails. It's widely used and tested so you shouldn't have a problem finding existing solutions whilst using it.
To inject it into your blog class:
class Blog {
private $_db;
public function __construct(PDO $db) {
$this->_db = $db
}
public function comment() {
return $this->_db->query(/*something*/);
}
}