I am following mvc structure in PHP and I want to retrieve last inserted row ID.
I have created following sql code:
$sql = "INSERT INTO song (artist, track, link) VALUES (:artist, :track, :link)";
$query = $this->db->prepare($sql);
$query->execute(array(':artist' => $artist, ':track' => $track, ':link' => $link));
echo $query->lastInsertId(); // To retrieve last inserted row ID.
but unfortunately I ma getting this error: Fatal error: Call to undefined method PDOStatement::lastInsertId()
I have also tried this stack links but not worked for me so I will happy if you help me for retrieve ID.
I am also sharing my controller.php file here.
/**
* This is the "base controller class". All other "real" controllers extend this class.
*/
class Controller{
/**
* #var null Database Connection
*/
public $db = null;
/**
* Whenever a controller is created, open a database connection too. The idea behind is to have ONE connection
* that can be used by multiple models (there are frameworks that open one connection per model).
*/
function __construct(){
$this->openDatabaseConnection();
}
/**
* Open the database connection with the credentials from application/config/config.php
*/
private function openDatabaseConnection(){
// set the (optional) options of the PDO connection. in this case, we set the fetch mode to
// "objects", which means all results will be objects, like this: $result->user_name !
// For example, fetch mode FETCH_ASSOC would return results like this: $result["user_name] !
// #see http://www.php.net/manual/en/pdostatement.fetch.php
$options = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ, PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING);
// generate a database connection, using the PDO connector
// #see http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/
$this->db = new PDO(DB_TYPE . ':host=' . DB_HOST . ';dbname=' . DB_NAME, DB_USER, DB_PASS, $options);
}
/**
* Load the model with the given name.
* loadModel("SongModel") would include models/songmodel.php and create the object in the controller, like this:
* $songs_model = $this->loadModel('SongsModel');
* Note that the model class name is written in "CamelCase", the model's filename is the same in lowercase letters
* #param string $model_name The name of the model
* #return object model
*/
public function loadModel($model_name){
require 'application/models/' . strtolower($model_name) . '.php';
// return new model (and pass the database connection to the model)
return new $model_name($this->db);
}
}
You're almost there.
If you look at the manual page for lastInsertId, it's called on the database handle - you're currently calling it on the statement.
You just need to call:
$this->db->lastInsertId();
you can try the following -
$query = "INSERT INTO song (artist, track, link) VALUES (:artist, :track, :link)";
$stmt = $pdo->prepare($query);
$params = array(
"artist" => $artist,
"track" => $track,
"link" => $link,
);
$data = $stmt->execute($params);
$insert_id = $pdo->lastInsertId();
Related
Having some trouble with PHP static functions in my program. My code is below and I keep getting the error "Anonymous function expected". I'm using the IDE PhpStorm.
I've had a google but nothing comes up. If I remove the name of the function the error goes away but I don't know how to then call the function.
<?php
/**
* #param mysqli $conn Connection to the database.
* #param int $id The ID of the white card that is to be voted up.
*/
public static function voteUp($conn, $id){
mysqli_query($conn, 'UPDATE WhiteCards SET Ups = Ups + 1 WHERE ID = ' . $id);
}
/**
* #param mysqli $conn Connection to the database.
* #param int $id The ID of the white card that is to be voted down.
*/
public static function voteDown($conn, $id){
mysqli_query($conn, 'UPDATE WhiteCards SET Ups = Ups - 1 WHERE ID = ' . $id);
}
Just solved it, the two functions needed to be in a class.
I own an mssql database server, and connect to it using doctrine2(sqlsrv)
I would like to create the new entity instances with a given id. But if I try it, I get an error:
Cannot insert explicit value for identity column in table 'my_test_table' when IDENTITY_INSERT is set to OFF
I've removed the #GeneratedValue annotation. But I still get this error.
After that, I've run this script in the `SQL Server management studio:
SET IDENTITY_INSERT my_test_table ON
Unfortunately I still get the error, and I can't understand why
It has to be called on the doctrine's connection
$em->getConnection()->prepare("SET IDENTITY_INSERT my_test_table ON")->execute();
Something may be different with my setup, or something in Doctrine may have changed, but this wouldn't work for me with Doctrine ORM 2.5.6, PHP 7.0.17, and SQL Server 2014.
Despite setting it before my flush, it wouldn't work. It also couldn't work for multiple tables from a class hierarchy as IDENTITY_INSERT can be on for only one table at a time.
I was able to figure out how to do this by using a wrapper class for the connection. Doctrine supports this with the wrapperClass configuration parameter. Below is my code that worked.
<?php
declare(strict_types=1);
namespace Application\Db;
/**
* Class SqlSrvIdentityInsertConnection
* This class is to enable Identity Insert when using Doctrine with SQLServer.
* Must use this class with the "wrapperClass" configuration option
* for EntityManager::create
*/
class SqlSrvIdentityInsertConnection extends \Doctrine\DBAL\Connection
{
private $tables = [];
private $enabled = [];
public function enableIdentityInsertFor(string $tableName)
{
$this->tables[] = $tableName;
$this->enabled[$tableName] = false;
}
private function setIdentityInsert(string $statement) {
// Must turn off IDENTITY_INSERT if it was enabled, and this table
// isn't in the query. Must do this first!
foreach($this->tables as $tableName) {
if (stristr($statement, "INSERT INTO $tableName") === false) {
if ($this->enabled[$tableName]) {
parent::exec("SET IDENTITY_INSERT " . $tableName . " OFF");
$this->enabled[$tableName] = false;
}
}
}
foreach($this->tables as $tableName) {
if (stristr($statement, "INSERT INTO $tableName") !== false) {
parent::exec("SET IDENTITY_INSERT ".$tableName." ON");
$this->enabled[$tableName] = true;
// Only one can be enabled at a time
return;
}
}
}
public function prepare($statement)
{
$this->setIdentityInsert($statement);
return parent::prepare($statement);
}
}
Here is how it is used when you want to insert some entities with
$em->persist($newEntity);
/** #var SqlSrvIdentityInsertConnection $conn */
$conn = $em->getConnection();
$metadata = $this->session->getClassMetaData(MyEntityClass::class);
$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);
$conn->enableIdentityInsertFor($metadata->getTableName());
$em->flush();
Can't figure this one out:
In the DB_driver.php (core system/database)
this is beginning of init function...
/**
* Initialize Database Settings
*
* #return bool
*/
public function initialize()
{
/* If an established connection is available, then there's
* no need to connect and select the database.
*
* Depending on the database driver, conn_id can be either
* boolean TRUE, a resource or an object.
*/
if ($this->conn_id)
{
return TRUE;
}
// ----------------------------------------------------------------
// Connect to the database and set the connection ID
$this->conn_id = $this->db_connect($this->pconnect);
The initialize() is called wihtin CI_Controller so when creating new objects for example like this:
$x = new PartyPooper();
$y = new PartyPooper();
initialize() in the DB_driver is called twice. Nothing strange with that, but I would expect $this->conn_id to be set when creating PartyPooper() object second time ($y)?
When is "an established connection" supposed to be true? (In the example two database connections are made when there would should only be one?)
I'm using the latest database drivers in the development branch: https://github.com/EllisLab/CodeIgniter/tree/develop/system/database
I am using the mysqli-driver with persistant connections of.
UPDATE:
I'm not a huge fan messing with core-files, but I couldn't figure out another solution here. Please tell me if there's a better way of achieving of what I want to do.
I came up with this code (using sessions to handle storing and checking for existing db-connections (of the "subdriver"-object. (mysqli object in my case)):
public function initialize()
{
/* If an established connection is available, then there's
* no need to connect and select the database.
*
* Depending on the database driver, conn_id can be either
* boolean TRUE, a resource or an object.
*/
if ($this->conn_id) {
return TRUE;
}
$conn_session_id_name = 'dbsession_conn';
if (isset($_SESSION[$conn_session_id_name])) {
$sess = $_SESSION[$conn_session_id_name];
// Set connection id object or resourse and return true
// because no more connecting has to be done
if (is_object($sess) || is_resource($sess)) {
$this->conn_id = $sess;
return TRUE;
}
}
// Connect to the database and set the connection ID
$this->conn_id = $this->db_connect($this->pconnect);
// Store conn object or resource etc into session
if (is_object($this->conn_id) || is_resource($this->conn_id)) {
$_SESSION[$conn_session_id_name] = $this->conn_id;
}
// No connection resource? Check if there is a failover else throw an error
if ( ! $this->conn_id)
{
//rest of code as before...
My application got extremly much faster because it used one connection instead of around 60.
But my original question remains:
When is this supposed to be true in the initialize() function?
if ($this->conn_id) {
return TRUE;
}
(I didn't remove it when changing the code because I suppose it has some purpose-but even if I can't figure out which)
UPDAtE2 - clarification:
In a model I have a db select statement that is supposed to return PartyPooper objects:
eg. $res = $q->result('PartyPooper');
This PartyPooper is a controller that stores information about people, like names, years , birthdays etc, but it also handles stuff like calculation of info within the same object.
class PartyPooper extends CI_Controller { .... }
But as I understand from the comments below I should do like this instead?
class PartyPooper extends CI_Controller { .... }
class PartyPooperObject { .... } //Store information about people in this object
eg. $res = $q->result('PartyPooperObject');
This piece of code
if ($this->conn_id) {
return TRUE;
}
just assures that the initialize method is only called once per object.
If it has been called on the current object already, it must not be executed another time.
Writing my first application in PHP that makes use of classes and objects.
I have a DB class which takes a string to select the appropriate config because there are multiple databases. I started out with a login class but scratched that idea in exchange for a user class so I can do user->isLoggedIn stuff. The user class uses MySQL which stores user and login information, as well as credentials for the second database.
$mysql = new db( 'mysql' );
$user = new user( $mysql );
if( !( $user->isLoggedIn() === true ) )
{
goToPage( 'login.php' );
exit();
}
The second database is Sybase and stores account information. I need the credentials from the user class to get lists and accounts information from here. I think an account class should be next but not sure of the best way to do it. Something like this maybe..
$sybase = new db( 'sybase' );
$account = new account( $sybase );
$account_list = $account->getList( $user->info );
$user->info being an array i guess of credentials and info needed for the account table, or is there a better way to go about this?
Edited to include db class example
config.db.php
$config_array = array();
$config_array[ 'mysql' ][ 'dsn' ] = "mysql:host=localhost;dbname=********;charset=UTF-8";
$config_array[ 'mysql' ][ 'user' ] = "********";
$config_array[ 'mysql' ][ 'pass' ] = "**************";
$config_array[ 'sybase' ][ 'dsn' ] = "odbc:DRIVER={Adaptive Server Anywhere 8.0};***************";
$config_array[ 'sybase' ][ 'user' ] = "**********";
$config_array[ 'sybase' ][ 'pass' ] = "*********";
class.db.php
public function __construct( $type )
{
require 'config.db.php';
$this->type = $type;
foreach( $config_array[ $this->type ] AS $key => $value )
{
$this->$key = $value;
}
try
{
$this->connection = new PDO( $this->dsn, $this->user, $this->pass, $this->options );
}
catch( PDOException $ex )
{
log_action( "db->" . $this->type, $ex->getCode() . ": " . $ex->getMessage() );
$this->error = true;
}
return $this->connection;
}
Does something like the following make sense?
class User()
{
public function getAccountList()
{
$this->Account = new Account( new Database( 'sybase' ) );
return $this->Account->list( $this->userid );
}
}
Also, Accounts have different sections (i.e. History & Notes, Financial Transactions, Documents) that will be different 'tabs' on the page. Should each one of those be a class too?
First up, as a secondary answer on the other answer by Dimitry:
The things you'll gain by having a Singleton pale to the things you lose. You get a global object, which every part of your program can read and modify, but you lost testability, readability, and maintainability.
Because the Singleton object is in fact global, you cannot accurately isolate the single units in your application (which is crucial for Unit Testing), because your function is dependant on other components, which are "magically" inserted into it.
You lose readability because method() may actually need other things to work (for instance, the user object needs a db instance, this makes new user($db) much more readable than new user(), because even without looking at the source code, I can tell what the method/object needs to work.
You lose maintainability because of the reason stated above as well. It's harder to tell which components are inserted via the Singleton than it is to see it in the function's "contract", for that reason, it'll be harder for future you and/or any other developer to read and refactor your code.
As a side note, it's considered good naming convention to name Class names with its first letter upper cased, I'll be using this convention from now on.
Let's start from the top. You have 2 possible states for your Db object, MysqlDb, and SybaseDb. This calls for polymorphism. You have an abstract class Db, and two concrete classes MysqlDb and SybaseDb inheriting from it. The instantiation of the correct Db object is the responsibility of a factory
class DbFactory {
private $db;
/**
* Create a new Db object base on Type, and pass parameters to it.
*
* #param string $type Type of database, Mysql or Sybase.
* #param string $dsn The DSN string corresponding to the type.
* #param string $user User credential
* #param string $pass Password credential
*
* #return Db
*/
public function create($type, $dsn, $user, $pass) {
if (!is_a($this->db, "Db")) {
$type = $type . "Db";
$this->db = new $type($dsn, $user, $pass);
}
return $this->db;
}
}
abstract class Db {
/**
* #param $dsn
* #param $user
* #param $pass
*/
public function __construct($dsn, $user, $pass) {
$this->db = new PDO("$dsn", $user, $pass);
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
}
}
class MysqlDb extends Db {
/*
* More specific, Mysql only implementation goes here
*/
}
class SybaseDb extends Db {
/*
* More specific, Sybase only implementation goes here
*/
}
Now you should ask yourself, who is responsible for getting the list of accounts? Surely they shouldn't fetch themselves (just as much as it isn't the user's responsibility to fetch its own data from the database). It's the User's responsibility to fetch these accounts, using the SybaseDb connection.
In fact, the User needs the following to work:
Sybase database connection - to fetch Account list. We'll pass the DbFactory instance to it, so that we can easily get the instance if it were already instantiated, and instantiate it if not.
State information (logged in status, user ID, user name, etc).
The User isn't responsible to set this data, it needs it in order to work.
So the User constructor should look like this (assuming "ID" and "name" fields):
User::__construct($id, $name, DbFactory $factory);
The User will have a field $accounts, which would hold an array of Account objects. This array would be populated with a User::getAccounts() method.
I'd make Account the member of User class. "User has an Account", right?
Also, speaking about classes, make sure that your two DBs are singletones, and get them via getInstance(), not __construct().
I want to store all my sessions in a DB and have read up on this and implemented the following class:
<?php
/**
* This class handles users sessions and stores the session in the DB rather than in a file. This way stops any
* shared host security problems that could potentially happen.
*/
class sessionHandler {
/**
* Initial constructor which takes the database object as a param, to do all the database stuff
* #param object $db The datase object
*/
public function __construct ($db) {
$this->db = $db;
$this->setHandler();
}
function setHandler() {
session_set_save_handler(array(&$this, "open"),
array(&$this, "close"),
array(&$this, "read"),
array(&$this, "write"),
array(&$this, "destroy"),
array(&$this, "clean")
);
}
/**
* Initiate a database object if necessary
*/
function open() {
$this->db->connect();
}
/**
* Write session id and data to the database
* #param string $id The hashed 32 char session id, unique to a user
* #param string $data Serialized session array from the unique session
* #return id The newly inserted ID of the database
*/
function write($id, $data) {
$access = time();
$dateAdded = date("Y-m-d G:i:s");
$this->db->wrapper->where(array("sessionId"=>$id));
$this->db->query($this->db->wrapper->delete(__CLASS__));
//fopen a file and store this in it that way we can debug
$query = $this->db->wrapper->insert(__CLASS__, array("sessionId"=>$id,"dateAdded"=>$dateAdded,"sessionData"=>$data));
$this->db->query($query);
return $this->db->insertId();
}
/**
* Retrieve the session data for a given session id
* #param string $id The hashed 32 char session id, unique to a user
* #return string The session data found for the given session id
*/
function read($id) {
$id = $this->db->wrapper->escape($id);
$row = $this->db->fetch(1, $this->db->wrapper->get_where(__CLASS__,array("sessionId"=>$id)), array(),false);
if ($row) {
return $row['data'];
}
return "";
}
/**
* Delete a session from the database by its unique session id
* #param string $id The hashed 32 char session id, unique to a user
* #return integer The number of deleted rows - should only ever be 1
*/
function destroy($id) {
$id = $this->db->wrapper->escape($id);
$this->db->wrapper->where(array("sessionId"=>$id));
$this->db->query($this->db->wrapper->delete(__CLASS__));
return $this->db->affectedRows();
}
/**
* Garage collector which deletes old records in the database, delete sessions that have expired. This is
* determined by the session.gc_maxlifetime variable in the php.ini
* #param integer $max The maximum number of seconds allowed before a session is to be considered expired
* #return integer The number of deleted rows
*/
function clean($max) {
$old = time() - $max;
$old = $this->db->wrapper->escape($old);
$this->db->wrapper->where(array("access"=>$old), "<");
$this->db->query($this->db->wrapper->delete(__CLASS__));
return $this->db->affectedRows();
}
/**
* Close the database connection once a read / write has been complete
*/
function close() {
$this->db->close();
}
/**
* End the current session and store session data.
*/
public function __destruct(){
session_write_close();
}
}
As you can see in my code, i pass the DB object into the class as a param. My bootstrap file is as follows:
$db = mogDB::init();
$sh = new sessionHandler($db);
session_start();
I have used a few of my own classes here so MogDB:init() basically creates a database connection with the right credentials. The wrapper stuff is basically so i dont have to type out sql query after sql query (me being a bit lazy i guess).
But the problem i get is this in my php error log:
08-Apr-2010 17:40:31] PHP Warning: mysql_insert_id(): 11 is not a valid MySQL-Link resource in /library/mysql.php on line 69
I have debugged this as much as i can and it seems that when it tries to write the session to the DB, it is failing. I have managed to save the query to a file and that imports fine into the database via phpmyadmin, so its not the query thats the problem.
line 69 in mysql.php is as follows:
68. public function insertId() {
69. return mysql_insert_id($this->id);
70. }
Any help would be much appreciated
Thanks
After you include the contents of the file /library/mysql.php around line 69 so we can see what is happening there, I am guessing that a small bit of your code is asking for a database handle back to call mysql_insert_id on, and instead, your db api is giving back the actual insertID already...