php mysqli::$insert_id -> not working properly - php

I wanted to insert a row and at the same time get the id generated by that query.
I'm just testing it first and so far here is what I got.
I created a class which has a database configuration like this
class Database{
private $host;
...
...
protected function connect(){
$this->host = 'localhost';
...
...
$conn = new mysqli($this->host, ...,);
if ($conn->connect_errno){
echo 'error';
};
return $conn;
}
}
A User class for the actual query and where I extend my Database class.
class User extends Database{
function createUser(...,){
$sql = "INSERT INTO t (field) VALUES ('value');";
$this->connect()->query($sql);
return $this->connect()->insert_id;
}
}
I already have data inserted in my table but the function returns 0.
Note:
I have my column ID on user table of AUTO_INCREMENT attribute.
The query is successful but not returning the correct ID.
I tried doing this without the Database class and it works fine, returning the ID generated by the query.
$mysqli = new mysqli('host', ...,);
if(result = $mysqli->query("INSERT INTO t (field) VALUES ('value');")){
return $mysqli->insert_id;
}

Problem is inside your Database class.
you need a $conn field.
calling connect() causes to create new connection every time.
class Database{
private $host;
private $conn;
...
...
protected function connect(){
$this->host = 'localhost';
...
...
$this->conn = new mysqli($this->host, ...,);
if ($this->conn->connect_errno){
echo 'error';
};
}
}
class User extends Database{
function createUser(...,){
$sql = "INSERT INTO t (field) VALUES ('value');";
$this->conn->query($sql);
return $this->conn->insert_id;
}
}

Don't do it this way! Your Datebase class is useless and it doesn't follow the best practices of OOP.
Your problem is that each time you call $this->connect() you create a new connection. The connections do not share state between themselves so you can't access the last inserted ID using a different connection. It has to be the same connection!
Don't write such classes like Database class unless you are actually going to write abstraction library, and never inherit from a Database class.
mysqli is a class in itself. To open a database connection using mysqli you only need 3 lines of code.
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new mysqli('localhost', 'user', 'pass', 'db');
$mysqli->set_charset('utf8mb4'); // always set the charset
You can then pass the instance to any class that needs it.
class User {
private mysqli $db;
public function __construct(mysqli $mysqli) {
$this->db = $mysqli;
}
function createUser(/* ... */) {
$stmt = $this->db->prepare("INSERT INTO t (field) VALUES (?)");
$stmt->bind_param('s', $value);
$stmt->execute();
return $this->db->insert_id;
}
}
$user = new User($mysqli);
You must always use prepared statements because it allows you to parameterize your queries. Never concatenate data directly into SQL.
Lastly, do not use mysqli on its own. It is not suitable for beginners nor to be used on its own. If you are only starting to learn PHP then you should learn PDO instead of mysqli. PDO is much easier and more suitable for beginners. Start here https://phpdelusions.net/pdo

Related

how to consolidated database connection into one file in php [duplicate]

I really hope someone can help me figure out what I am missing. I have upgraded my installation from PHP 5.6 to 7.0 and this has forced me to update from Mysql to Mysqli which for some reason has broken my setup.
I researched and followed this guide "Using MySQLi in other classes": Using MySQLi in other classes
I am writing as a last resort and have looked at other sites as well but it seems like the problem comes some where else from.
First I have a database class:
private $serverName = "localhost";
private $userName = "DBUserName";
private $pass = "UserPassword";
private $database = "SelectedDB";
public $conn;
public function __construct(){
$this->conn = new mysqli($this->serverName, $this->userName,$this->pass,$this->database);
}
Then I have an API class where I want to access this connection which looks like
require_once 'Database.php';
class MyAPI{
private $db;
public function __construct($request_uri, $postData, $origin) {
$this->db = new Database();
}
and lastly i try to call it from a function:
$getUserResult = mysqli_query( $this->db->conn, $getUserQry);
When ever I call $this->db->conn I get an internal server error 500
If I create the database connection in the MyAPI class there is no issues which seems odd to me.
I hope someone can point me in a direction.
UPDATE:
I corrected a spelling error in the script and now I get 200 but the value still continues to be null from the query mysqli_query.
If i create the $dbtest = new database(); and use that instead it works fine. Is there someway to get it to work inside the constructor with the reference to $db?
There are several bad practices that led you to this error.
Clearly, extending User from a Database is a wrong move. Also, the whole Database class is rather useless as it doesn't do anything useful.
Hence I would suggest to
get rid of the useless Database class.
create a single $db instance from vanilla mysqli.
pass it as a constructor parameter into every class that needs a database connection
database.php:
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$db = new mysqli("localhost", "DBUserName", "UserPassword", "SelectedDB");
$db->set_charset('utf8mb4');
myapi.php
<?php
class MyAPI
{
protected $db;
public function __construct($db, $request_uri, $postData, $origin)
{
$this->db = $db;
}
public function getUser($id)
{
$sql = "SELECT * FROM users where id=?";
$stmt = $this->db->prepate($sql);
$stmt->bind_param("s", $id);
$stmt->execute();
$result = $stmt->get_result();
return $result->fetch_assoc();
}
}
app.php
<?php
# require_once 'Database.php';
# require_once 'myapi.php';
require 'vendor/autoload.php'; // autoloading is a must
$api = new MyAPI($db, $request_uri, $postData, $origin);
$user = $api->getUser($_POST['id']);

Prepared statement in OOP [duplicate]

I really hope someone can help me figure out what I am missing. I have upgraded my installation from PHP 5.6 to 7.0 and this has forced me to update from Mysql to Mysqli which for some reason has broken my setup.
I researched and followed this guide "Using MySQLi in other classes": Using MySQLi in other classes
I am writing as a last resort and have looked at other sites as well but it seems like the problem comes some where else from.
First I have a database class:
private $serverName = "localhost";
private $userName = "DBUserName";
private $pass = "UserPassword";
private $database = "SelectedDB";
public $conn;
public function __construct(){
$this->conn = new mysqli($this->serverName, $this->userName,$this->pass,$this->database);
}
Then I have an API class where I want to access this connection which looks like
require_once 'Database.php';
class MyAPI{
private $db;
public function __construct($request_uri, $postData, $origin) {
$this->db = new Database();
}
and lastly i try to call it from a function:
$getUserResult = mysqli_query( $this->db->conn, $getUserQry);
When ever I call $this->db->conn I get an internal server error 500
If I create the database connection in the MyAPI class there is no issues which seems odd to me.
I hope someone can point me in a direction.
UPDATE:
I corrected a spelling error in the script and now I get 200 but the value still continues to be null from the query mysqli_query.
If i create the $dbtest = new database(); and use that instead it works fine. Is there someway to get it to work inside the constructor with the reference to $db?
There are several bad practices that led you to this error.
Clearly, extending User from a Database is a wrong move. Also, the whole Database class is rather useless as it doesn't do anything useful.
Hence I would suggest to
get rid of the useless Database class.
create a single $db instance from vanilla mysqli.
pass it as a constructor parameter into every class that needs a database connection
database.php:
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$db = new mysqli("localhost", "DBUserName", "UserPassword", "SelectedDB");
$db->set_charset('utf8mb4');
myapi.php
<?php
class MyAPI
{
protected $db;
public function __construct($db, $request_uri, $postData, $origin)
{
$this->db = $db;
}
public function getUser($id)
{
$sql = "SELECT * FROM users where id=?";
$stmt = $this->db->prepate($sql);
$stmt->bind_param("s", $id);
$stmt->execute();
$result = $stmt->get_result();
return $result->fetch_assoc();
}
}
app.php
<?php
# require_once 'Database.php';
# require_once 'myapi.php';
require 'vendor/autoload.php'; // autoloading is a must
$api = new MyAPI($db, $request_uri, $postData, $origin);
$user = $api->getUser($_POST['id']);

Set $mysqli as a global variable for OOP

Ok,
This is sort of an involved problem, but any help or advice would be incredibly appreciated.
So I'm working with a site that (using .htaccess) redirects all traffic to a load.php. For any sql functionality, I have an abstract class that has a lot of query statements as functions that pass parameters to define the specifics of each query.
e.g.
$table->update("constraints")
I'm trying to figure out how to set the connection to the database on load.php, and then set the connection as a variable ($mysqli) that can then be referenced in my abstract query class without having to pass the parameter to every single query function call.
Again, any help or advice would be appreciated.
Here's an example of a function:
function clearTable (){
$mysqli = dbConnect::connect();
$sql = "TRUNCATE TABLE $this->tablename";
$mysqli->query($sql);
}
If I connect to the database in a construct function and set $this->mysqli and replace $mysqli = dbConnect::connect(); with $mysqli = $this->mysqli, none of the queries work. Though they work with a fresh reconnect on each call.
You should use Dependency Injection for this.
Basically it means that the class that needs the database connection doesn't create the connection, it just receives the already instasiated instance.
Example
In some init file:
// Create the db connection
$db = new Mysqli(......);
// Pass it to the query class that needs it
$queryClass = new QueryClass($db);
Then in your class file:
class QueryClass
{
protected $db;
public function __construct($db)
{
// $this->db will now be the same Mysql instance
$this->db = $db;
}
public function doSomeQuery()
{
$this->db->query(....);
}
}
A bonus for this is that you don't need to touch the QueryClass, if you ever want to start making some unit tests. You only need to pass a DB connection to a test database instead.
After looking through this a bit more, I can also create my db::connect() function to look like this:
class dbConnect {
private static $db;
private $mysqli;
private function __construct() {
$this->mysqli = new mysqli(HOST, USER, PASSWORD, DATABASE);
}
function __destruct() {
$this->mysqli->close();
}
public static function connect() {
if (self::$db == null) {
self::$db = new dbConnect();
}
return self::$db->mysqli;
}
}
and pass that as $this->mysqli in the query functions file

Accessing a PDO instance from another class

Here is my db class for connecting with database using pdo ext.
class db
{
private $host;
private $dbname;
private $username;
private $password;
public function __construct($host,$db,$name,$pass)
{
$this->host=$host;
$this->dbname=$db;
$this->username=$name;
$this->password=$pass;
$dsn = 'mysql:'.$this->dbname.';'.$this->host;
try
{
$conn = new PDO($dsn, $this->username, $this->password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $e)
{
echo 'ERROR: ' . $e->getMessage();
}
}
}
I call db class in login class like this...
$host='localhost';
$db='techy_issue_tracker';
$name='root';
$pass='';
$base= new db($host,$db,$name,$pass);
Here is the problem, to make a pdo query inside login class (extending db class) if I use a line this...
$stmnt = $conn->prepare('SELECT id FROM users WHERE name :name OR email = :email');
It generates two errors, saying..
Notice: Undefined variable: conn... and
Fatal error: Call to a member function prepare() on a non-object...
I can fix this by simply putting all pdo stuff inside login class but still I am just curious...how do you guys call an object (which is an instance of pdo class?) from another class.
PHP Class Based User System With PDO - Call to a member function prepare() on a non-object This question is interesting but didn't understand much :/
Didn't practice OOP much so some good explanation would be appreciated! Thanks.
You need to intialize $conn as a public property of your db class.
After the private properties in your db class, add this:
public $conn;
Then, inside your try catch, change $conn to $this->conn:
$this->conn = new PDO($dsn, $this->username, $this->password);
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Then, assuming you've correctly extended the db class, you can access conn in this manner:
$stmt = $db->conn->prepare('SELECT id FROM users WHERE name :name OR email = :email');
Why don't you just add a method to the db class that takes in a sql string and returns an array of object. Just do all the statement thingy in that method. Then the login class can just call this method to do any query. It does not need to know how the database connection is implemented. This way, if you want to change the underlying implementation in future. It only affects the db class. Not much changes is to be done to the login class as all database accesses goes through the db class.
There's an error in your WHERE clause:
WHERE name :name
You're missing an equal sign:
WHERE name = :name

Converting mysql to mysqli - how to get superglobal connection object?

I am trying to convert code from mysql to mysqli.
The code uses a single mysql_connect in a file which is included by every other file.
mysql_connect returns a MySQL link identifier that is a superglobal so you can rely on having a database connection available in any of your own functions.
It looks like with mysqli_connect this is not the case, the object returned isn't global.
Does this mean I have to add : global $mysqli; at the top of every function, or is there an way of making it a superglobal?
Relying on the fact that PHP will use the last opened connection resource if you don't specify one, is probably not a very good idea.
What happens if your application changes and you need two connections, or the connection is not there?
So it seems you need to do some refactoring anyway.
Here's a solution similar to Karsten's that always returns the same mysqli object.
class DB {
private static $mysqli;
private function __construct(){} //no instantiation
static function cxn() {
if( !self::$mysqli ) {
self::$mysqli = new mysqli(...);
}
return self::$mysqli;
}
}
//use
DB::cxn()->prepare(....
I usually make a function:
$mysqli = new mysqli(...);
function prepare($query) {
global $mysqli;
$stmt = $msqyli->prepare($query);
if ($mysqli->error()) {
// do something
}
return $stmt;
}
function doStuff() {
$stmt = prepare("SELECT id, name, description FROM blah");
// and so on
}
and then call that. That being said, I've since abandoned mysqli as being too bug-ridden to be considered usable. Shame really.
A very simple way to do this would be with a fixed database class, just to hold the mysqli connection object:
class Database {
public static $connection;
}
Database::$connection = new mysqli(HOST, USERNAME, PASSWORD, DATABASE);
Then you can access it in the normal ways:
$sql = 'SELECT * FROM table';
$result = Database::$connection->query($sql);
$result = mysqli_query(Database::$connection, $sql);
echo 'Server info ' . mysqli_get_server_info(Database::$connection);
To introduce some oop to you and solve your problem, you could use a class like this:
class MyDatabase
{
private static $_connection;
public static function connect()
{
$mysqli = new mysqli(...);
self::$_connection = $mysqli;
}
public static function getConnection()
{
return self::$_connection;
}
}
In your database-connection file you would load this class and execute MyDatabase::connect(); once.
To get the $mysqli-connection anywhere in your script, just call MyDatabase::getConnection();.

Categories