Returning lastInsertId() from database class - php

I am using a database class picked up from a tutorial on codecourse.com (I am just starting the migration from procedural to pdo) and I am slowly extending it to fit my needs. However, the one thing I cannot manage is to return the lastInsertId() to be used globally.
Using the register example from that tutorial
$user = new User();
$salt = Hash::salt(32);
try {
$user->create(array(
'username' => Input::get('username'),
'password' => Hash::make(Input::get('password'), $salt),
'salt' => $salt,
'firstname' => Input::get('first_name'),
'lastname' => Input::get('last_name'),
'joined' => date('Y-m-d H:i:s'),
'group' => 1
));
} catch(Exception $e) {
die($e->getMessage());
}
It is at this point that I want to get the lastInsertId() - the one of the just registered user. I am not sure whether it comes out of the Database class via the insert function
require_once 'core/init.php';
class DB {
private static $_instance = null;
private $_pdo,
$_query,
$_error = false,
$_results,
$_count = 0;
private function __construct() {
try {
$this->_pdo = new PDO('mysql:host=' . Config::get('mysql/host') . ';dbname=' . Config::get('mysql/db'), Config::get('mysql/username'), Config::get('mysql/password') );
} catch(PDOException $e) {
die($e->getMessage());
}
}
public static function getInstance() {
if(!isset(self::$_instance)) {
self::$_instance = new DB();
}
return self::$_instance;
}
public function query ($sql, $params = array()) {
$this->error = false;
if ($this->_query = $this->_pdo->prepare($sql)) {
$x = 1;
if(count($params)) {
foreach($params as $param) {
$this->_query->bindValue($x, $param);
$x++;
}
}
if($this->_query->execute()) {
$this->_results = $this->_query->fetchAll(PDO::FETCH_OBJ);
$this->_count = $this->_query->rowCount();
} else {
$this->_error = true;
}
}
return $this;
}
public function action ($action, $table, $where = array()) {
if (count($where) === 3) {
$operators = array('=', '>', '<', '>=', '<=');
$field = $where[0];
$operator = $where[1];
$value = $where[2];
if (in_array($operator, $operators)) {
$sql = "{$action} FROM {$table} WHERE {$field} {$operator} ?";
if(!$this->query($sql, array($value))->error()) {
return $this;
}
}
}
return false;
}
public function get ($table, $where) {
return $this->action('SELECT *', $table, $where);
}
public function delete ($table, $where) {
return $this->action('DELETE', $table, $where);
}
public function insert ($table, $fields = array()) {
$keys = array_keys($fields);
$values = '';
$x = 1;
foreach($fields as $field) {
$values .= '?';
if ($x < count($fields)) {
$values .= ', ';
}
$x++;
}
$sql = "INSERT INTO {$table} (`" . implode('`,`', $keys) . "`) VALUES ({$values})";
if (!$this->query($sql,$fields)->error()) {
return true;
}
echo $sql;
}
return false;
}
public function update ($table, $id, $fields = array()) {
$set = '';
$x = 1;
foreach ($fields as $name => $value) {
$set .= "{$name} = ?";
if ($x < count($fields)) {
$set .= ', ';
}
$x++;
}
$sql = "UPDATE {$table} SET {$set} WHERE id = {$id}";
if (!$this->query($sql,$fields)->error()) {
return true;
}
}
return false;
}
public function first () {
return $this->results()[0];
}
public function results () {
return $this->_results;
}
public function count () {
return $this->_count;
}
public function error () {
return $this->_error;
}
}
Or the User class via the create function
class User {
private $_db,
$_data,
$_sessionName,
$_cookieName,
$_isLoggedIn;
public function __construct($user = null) {
$this->_db = DB::getInstance();
$this->_sessionName = Config::get('session/session_name');
$this->_cookieName = Config::get('remember/cookie_name');
if (!$user) {
if (Session::exists($this->_sessionName)) {
$user = Session::get($this->_sessionName);
if ($this->find($user)) {
$this->_isLoggedIn = true;
} else {
//Logged out
}
}
} else {
$this->find($user);
}
}
public function update($fields=array(), $id = null) {
if (!$id && $this->isLoggedIn ()) {
$id = $this->data()->id;
}
if (!$this->_db->update('users', $id, $fields)) {
throw new Exception('There was a problem updating the account!');
}
}
public function create($fields) {
if (!$this->_db->insert('users', $fields)) {
throw new Exception('There was a problem creating an account!');
}
}
public function find($user=null) {
if ($user) {
$field = (is_numeric($user)) ? 'id' : 'username';
$data = $this->_db->get('users', array($field, '=', $user));
if ($data->count()) {
$this->_data = $data->first();
return true;
}
}
return false;
}
public function login($username=null, $password=null, $remember = false) {
if(!$username && !$password && $this->exists()) {
Session::put($this->_sessionName, $this->data()->id );
} else {
$user = $this->find($username);
if ($this->data()->password === Hash::make($password, $this->data()->salt)) {
Session::put($this->_sessionName, $this->data()->id);
if ($remember) {
$hash = Hash::unique();
$hashCheck = $this->_db->get('users_session', array('user_id', '=', $this->data()->id));
if (!$hashCheck->count()) {
$this->_db->insert('users_session', array(
'user_id' => $this->data()->id,
'hash' => $hash
));
} else {
$hash = $hashCheck->first()->hash;
}
Cookie::put($this->_cookieName, $hash, Config::get('remember/cookie_expiry'));
}
return true;
}
return false;
}
}
public function hasPermission ($key) {
$group = $this->_db->get('groups', array('id', '=', $this->data()->group));
if($group->count()) {
$permissions = json_decode($group->first()->permissions, true);
if ($permissions[$key] == true) {
return true;
}
}
return false;
}
public function exists () {
return (!empty($this->_data)) ? true : false;
}
public function logout () {
Session::delete($this->_sessionName);
}
public function data () {
return $this->_data;
}
public function isLoggedIn () {
return $this->_isLoggedIn;
}
}
I have tried in both but whenever I try to echo the lastInsertId() back out, nothing is returned. Any advice would be greatly welcomed. If the problem might be outside of these areas, I have uploaded the entire script to https://github.com/MargateSteve/login.
Thanks in advance
Steve

The insert() method of a database class should return insert id. Here is a relevant part for it:
public function insert ($table, $fields = array()) {
$this->query($sql,$fields);
return $this->_db->lastInsertId;
}
while create() method of a User class should create a user instance.
public function create($fields) {
$id = $this->_db->insert('users', $fields);
$this->find($id);
}
Note that insert() method is vulnerable to SQL injection.
And now you can use your newly created user all right
$user = new User();
$user->create(array(
'username' => Input::get('username'),
'password' => Hash::make(Input::get('password')),
'firstname' => Input::get('first_name'),
'lastname' => Input::get('last_name'),
'joined' => date('Y-m-d H:i:s'),
'group' => 1
));
echo $user->data['id'];
I hope you are looking not for the code to copy and paste but for the understanding. And I hope you understand the logic above.

Add a public variable to your DB class that will hold the last inserted record ID:
class DB {
public $lastInsertId = null;
In the same DB class modify the query method, where the actual insert happens so that you can grab the ID from PDO:
public function query ($sql, $params = array()) {
$this->error = false;
if ($this->_query = $this->_pdo->prepare($sql)) {
$x = 1;
if(count($params)) {
foreach($params as $param) {
$this->_query->bindValue($x, $param);
$x++;
}
}
if($this->_query->execute()) {
$this->lastInsertId = $this->_pdo->lastInsertId();
$this->_results = $this->_query->fetchAll(PDO::FETCH_OBJ);
$this->_count = $this->_query->rowCount();
} else {
$this->_error = true;
}
}
return $this;
}
On the code above this is the important line:
$this->lastInsertId = $this->_pdo->lastInsertId();
You are assigning the value of PDO::lastInsertId() to your lastInsertId instance variable, that way you can access it from wherever you instantiate a DB object.
Now, modify the user class to hold a variable called id as well, do not name it lastInsertId because its confusing; in this context you have one single instance of a user which represents a single user and hence id simply refers to this instance user id:
class User {
public $id = null;
Modify in this same User class the create method as well to grab the lastInsertId value from your db object instance:
public function create($fields) {
if (!$this->_db->insert('users', $fields)) {
throw new Exception('There was a problem creating an account!');
}
$this->id = $this->_db->lastInsertId;
}
Then you can access the user ID in your register.php file simply accesing the user instance variable e.g. $user->id :
try {
$user->create(array(
'username' => Input::get('username'),
'password' => Hash::make(Input::get('password'), $salt),
'salt' => $salt,
'firstname' => Input::get('first_name'),
'lastname' => Input::get('last_name'),
'joined' => date('Y-m-d H:i:s'),
'group' => 1
));
Session::flash('home', "You have registered with user ID $user->id");
Redirect::to('index.php');
} catch(Exception $e) {
die($e->getMessage());
}

Related

Code won't run once adding transactions

Hello I have code that without transactions executes but with transactions doesn't run. My DB Class encapsulates prepared statements from which it is composed in my BaseApp Class and this is in turn is extended by BaseTransactApp Class.
BasetransactApp Class is extended by the EqRqst Class. EqRqst Class is extended by the EqLeaseRequest Class which is used to call the code in the trait with the problematic code.
At one point it showed the error 2SQLSTATE[HY000]: General error
when I did not set
$this->_pdo->setAttribute( PDO::ATTR_EMULATE_PREPARES , false);
My DB Class is
<?php
class DB{
`enter code here` private static $_instance = null;
private $_pdo,
$_query,
$_error = false,
$_results,
$x,
$_count = 0;
private function __construct(){
try{
$this->_pdo = new PDO('mysql:host=' .Config::get('mysql/host') . ';dbname=' . Config::get('mysql/db'), Config::get('mysql/username'), Config::get('mysql/password'));
$this->_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->_pdo->setAttribute( PDO::ATTR_EMULATE_PREPARES , false);
}catch(PDOException $e){
die($e->getMessage());
}
}
public static function getInstance(){
if(!isset(self::$_instance)){
self::$_instance = new DB();
}
return self::$_instance;
}
public function query($sql, $params = array()){
$this->_error = false;
if($this->_query = $this->_pdo->prepare($sql)){
$x = 1;
if(count($params)){
foreach($params as $param){
$this->_query->bindValue($x,$param);
$x++;
}
}
if($this->_query->execute()){
$this->_results = $this->_query->fetchAll(PDO::FETCH_OBJ);
$this->_count = $this->_query->rowCount();
}else{
$this->_error = true;
}
}
return $this;
}
public function action($action,$table,$where = array()){
if(count($where) === 3){
$operators = array('=','>','<','>=','<=');
$field = $where[0];
$operator = $where[1];
$value = $where[2];
if(in_array($operator , $operators)){
$sql = "{$action} FROM {$table} WHERE {$field} {$operator} ? ";
if(!$this->query($sql, array($value))->error()){
return $this;
}
}
}
return false;
}
public function update($table,$id,$fields){
$set = '';
$x = 1;
foreach($fields as $name => $value){
$set .= "{$name} = ?";
if($x < count($fields)){
$set .= ', ';
}
$x++;
}
$sql = "UPDATE {$table} SET {$set} WHERE {$name} = '{$id}'";
echo $sql;
if(!$this->query($sql,$fields)->error()){
echo "true";
return true;
}
return false;
}
public function get($table,$where){
return $this->action('SELECT *',$table,$where);
}
public function delete($table,$where){
return $this->action('DELETE',$table,$where);
}
public function insert($table,$fields = array()){
if(count($fields)){
echo 'this is the count '.count($fields);
$x=1;
if(!is_multi_array($fields)){
echo 'Not a multi array'.'<br>';
$keys = array_keys($fields);
print_r($keys);
$values = '';
}
foreach($fields as $field => $fieldValue){
if(!is_array($fieldValue)){
echo $x;
$values .="?";
if($x< count($fields)){
$values .= ', ';
}
if($x == count($fields)){
echo 'insert query is Executed'.'<br>';
echo $x;
$sql ="INSERT INTO {$table} (`".implode('`,`',$keys). "`) VALUES ({$values})";
if(!$this->query($sql,$fields)->error()){
echo "true";
return true;
}
}
$x++;
}else{
if($x <= count($fields)){
echo 'recursive insert called'.'<br>';
echo "number of recursive cycles is ".$x;
$this->insert($table,$fieldValue);
if($x == count($fields)){
echo "Escaped";
exit();
}
$x++;
}
}
}
}
echo 'No no';
return false;
}
public function getLastId(){
return $this->_pdo->lastInsertId();
}
public function beginTransaction(){
return $this->_pdo->beginTransaction();
}
public function commit(){
return $this->_pdo->commit();
}
public function rollBack(){
return $this->_pdo->rollBack();
}
}
?>
IT is composed in BaseApp Class
protected $db,
$data,
$table;
public function __construct(){
$this->db = DB::getInstance();
}
public function getProp($prop){
return $this->$_prop;
}
public function setProp($objVar,$val){
$this->objVar = $val;
}
public function create($fields = array()){
if(!$this->db->insert($this->table, $fields) ){
throw new Exception('There was a problem creating an account.');
}
}
public function get($where = array()){
$dbResult = $this->db->get($this->table, $where);
if($dbResult->count()){
$this->data = $dbResult->first();
return true;
}
return false;
}
public function update($fields = array(),$id = null){
if(!$this->db->update($this->table,$id,$fields)){
throw new Exception('There was a problem Updating');
}
}
public function delete($where = array()){
$this->db->delete($this->table,$where);
}
public function data(){
return $this->data;
}
public function getTable(){
return $this->table;
}
public function getLastInsertId(){
return $this->db->getLastId();
}
}
?>
which is extended by
public function __construct(){
parent::__construct();
if($this->db){
echo 'WWWWWWWWWWWWWWWW';
}
}
public function beginTransaction(){
return $this->db->beginTransaction();
}
public function commit(){
return $this->db->commit();
}
public function rollBack(){
return $this->db->rollBack();
}
}
?>
Then
protected $EqRqst_ID,
$DateOfRequest,
$rqstState,
$RequestedPackage = array(),
$state_ID,
$table = 'EqRqst';
public function __construct(RqstState $state){
parent::__construct();
$this->rqstState = $state;
}
public function create($args = array()){
BaseTransactApp::create($args);
// $this->EqRqst_ID = $this->getLastInsertId();
// echo $this->EqRqst_ID;
}
public function getRqstID(){
return $this->EqRqst_ID;
}
public abstract function makeRequest($requestArg = array(),$elRqst = array(),$rqstPakg = array());
}
?>
This trait contains the code that is Use by eqleaserequest .This code executes well without transactions but fails to do so with them
public function makeRequest($requestArg = array(),$eqLsRqst = array(),$rqstPakg = array()){
try{
$this->beginTransaction();
parent::create($requestArg);
$this->EqRqst_ID = parent::getLastInsertId();
$this->create($eqLsRqst);
$this->_RequestPkg->create(multi_array_merge(array('EqRqst_ID'=> $this->EqRqst_ID),$rqstPakg));
$this->commit();
}catch(Exception $e){
$this->rollBack();
echo $e->getMessage().;
}
//$this->EqRqst_ID = $this->getRqstID();
}
This code is the problem it runs when I comment out the Transaction code but doesn't with it. IT Executed by
This class is the one that uses the TraitRequest
use traitRequest;
//protected $table = 'EqLeaseRequest';
private $_Emp,
$_Emp_ID,
$_RequestPkg,
$_Intended_Use,
$_EqRqst,
$_EqRqst_ID,
$_Expected_Returndate;
public $rqstID;
public function __construct(RqstState $state,Employee $Emp,RequestPackage $RqstPkg){
parent::__construct($state);
$this->_Emp = $Emp;
$this->_RequestPkg = $RqstPkg;
}
public function create($eqLsRqstArg = array()){
$this->table = 'EqLeaseRequest';
print_r(array_merge(array('EqRqst_ID'=> $this->EqRqst_ID),$eqLsRqstArg));
BaseTransactApp::create(array_merge(array('EqRqst_ID'=>$this->EqRqst_ID),$eqLsRqstArg));
//BaseTransactApp::create($eqLsRqstArg);
}
}
}
?>
I was able to find the solution by changing the code in the Trait traitRequest.
The solution is as follows
trait traitRequest{
public function makeRequest($requestArg = array(),$eqLsRqst = array(),$rqstPakg = array()){
$this->beginTransaction();
parent::create($requestArg);
if(parent::count()){
$this->EqRqst_ID = parent::getLastInsertId();
$this->create($eqLsRqst);
if($this->count()){
foreach($rqstPakg as $pkg){
$this->_RequestPkg->create(array_merge(array('EqRqst_ID'=> $this->EqRqst_ID),$pkg));
if(!$this->_RequestPkg->count()){
$this->rollBack();
}
}
$this->commit();
}else{
echo 'Second Rollback';
$this->rollBack();
}
}else{
echo 'First Rollback';
$this->rollBack();
}
}
}
?>

Retrieve data from database by class

I would like to learn programming, so I downloaded a safe script built in OOP to manage the login and user registration, I would like to get their hands to learn this language through classes and objects.
The script is this: https://github.com/aguvasu/Secure-PHP-OOP-user-reg-login-system-with-Boostrap
I come down to it, my problem where I can not find solution is that I would like to create a page where member.php show the list of registered users (username, email, etc.), but do not know how to do.
The class User.php shows this:
class User {
private $_db, $_data, $_sessionName, $_cookieName, $_isLoggedIn;
function __construct($user = null) {
$this->_db = DB::getInstance();
$this->_sessionName = Config::get('session/session_name');
$this->_cookieName = Config::get('remember/cookie_name');
if (!$user) {
if (Session::exists($this->_sessionName)) {
$user = Session::get($this->_sessionName);
if ($this->find($user)) {
$this->_isLoggedIn = true;
} else {
//logout
}
}
} else {
$this->find($user);
}
}
public function find($user = null) {
if ($user) {
$field = (is_numeric($user)) ? 'id' : 'username' ;
$data = $this->_db->get('users', array($field, '=', $user));
if ($data->count()) {
$this->_data = $data->first();
return true;
}
}
return false;
}
public function create($fields = array()) {
if (!$this->_db->insert('users', $fields)) {
throw new Exception("Error Processing Request", 1);
}
}
public function login($username = null, $password = null, $remember = null) {
if (!$username && !$password && $this->exists()) {
Session::put($this->_sessionName, $this->data()->id);
} else {
$user = $this->find($username);
if ($user) {
if ($this->data()->password === Hash::make($password, $this->data()->salt)) {
Session::put($this->_sessionName, $this->data()->id);
if ($remember) {
$hash = Hash::unique();
$hashCheck = $this->_db->get('users_sessions', array('user_id', '=', $this->data()->id));
if (!$hashCheck->count()) {
$this->_db->insert('users_sessions', array(
'user_id' => $this->data()->id,
'hash' => $hash));
} else {
$hash = $hashCheck->first()->hash;
}
Cookie::put($this->_cookieName, $hash, Config::get('remember/cookie_expiry'));
}
return true;
}
}
}
return false;
}
public function update($fields = array(), $id = null) {
if (!$id && $this->isLoggedIn()) {
$id = $this->data()->id;
}
if (!$this->_db->update('users', $id, $fields)) {
Session::flash('uperr', 'We experienced an error while updating your profile');
Redirect::to('account.php');
exit();
}
}
public function hasPermission($key) {
$group = $this->_db->get('groups', array('id', '=', $this->data()->user_group));
if ($group->count()) {
$permissions = json_decode($group->first()->permissions, true);
if ($permissions[$key]) {
return true;
}
}
return false;
}
public function logout() {
$this->_db->delete('users_sessions', array('user_id', '=', $this->data()->id));
Session::delete($this->_sessionName);
Cookie::delete($this->_cookieName);
}
public function data() {
return $this->_data;
}
public function isLoggedIn()
{
return $this->_isLoggedIn;
}
public function exists() {
return (!empty($this->_data)) ? true : false ;
}
}

Proper way to access database in PHP classes

My limited PHP knowledge is rather old. When I used to create websites I always had a config.php file which contained some defines and a $_DB global variable. Then in every function_xyz.php file I included this config file.
Now I want to finally move on and use classes. But I can't figure out a proper way to have access to mysql in functions of my classes without inclusion of the so called config.php file on top of each file.
Imagine I have a class called User.php:
class User {
private $firstName;
private $familyName;
private $emailAddress;
public function __construct($username, $password) {
//check if user with name and pass exist in DB
// stuff....
//If user exist, populate member variables
$this->emailAddress = ...
}
public function getEmail(){
return $this->emailAddress;
}
}
I know it is not the best example or practice...but how can I have a global MySQL access in all my classes without being required to have the config file included.
What is the best practice nowadays?
Make a global Instance:
//db.php include once
class DB {
#bind connenction in it
}
#make instance
$db = new DB($config);#use for User Instances
and then:
class User {
private $db;
private $firstName;
private $familyName;
private $emailAddress;
public function __construct($db) {
$this->db=$db;
}
public function validate($username, $password,$db) {
//check if user with name and pass exist in DB
//If user exist, populate member variables
$this->emailAddress = ...
}
public function getEmail(){
return $this->emailAddress;
}
}
$user = new User($db);
Is one way.
But you telling to less about how you want to use the classes.
I would go with PHPAcademy's login/register tutorial. He have DB class that handles almost anything you need. Here is sample of his code, slightly modified by me, but all credits to Alex Garrett.
<?php
class DB {
private static $_instance = null;
private $_pdo,
$_query,
$_error = false,
$_results,
$_count = 0;
private function __construct() {
try {
$this->_pdo = new PDO('mysql:host=' . Config::get('mysql/host') . ';dbname=' . Config::get('mysql/db'), Config::get('mysql/username'), Config::get('mysql/password'));
} catch(PDOException $e) {
die($e->getMessage());
}
}
public static function getInstance() {
if(!isset(self::$_instance)) {
self::$_instance = new DB();
}
return self::$_instance;
}
public function query($sql, $params = array()) {
$this->_error = false;
if($this->_query = $this->_pdo->prepare($sql)) {
$x = 1;
if(count($params)) {
foreach($params as $param) {
if (is_int($param)) {
$this->_query->bindValue($x, $param, PDO::PARAM_INT);
} else {
$this->_query->bindValue($x, $param);
}
$x++;
}
}
if($this->_query->execute()) {
$this->_results = $this->_query->fetchAll(PDO::FETCH_OBJ);
$this->_count = $this->_query->rowCount();
}
else {
$this->_error = true;
print_r($this->_query->errorInfo());
}
}
return $this;
}
public function action($action, $table, $where = array()) {
if(count($where) === 3){
$operators = array('=', '>', '<', '>=', '<=', '!=');
$field = $where[0];
$operator = $where[1];
$value = $where[2];
if(in_array($operator, $operators)) {
$sql = "{$action} FROM {$table} WHERE {$field} {$operator} ?";
if(!$this->query($sql, array($value))->error()) {
return $this;
}
}
} else if (count($where) === 0) {
$sql = "{$action} FROM {$table}";
if(!$this->query($sql)->error()) {
return $this;
}
}
return false;
}
public function get($table, $where) {
return $this->action('SELECT *', $table, $where);
}
public function delete($table, $where) {
return $this->action('DELETE', $table, $where);
}
public function getAll($table) {
return $this->action('SELECT *', $table);
}
public function first() {
return $this->results()[0];
}
public function last() {
$i = count($this->results()) - 1;
return $this->results()[$i];
}
public function insert($table, $fields = array()) {
if(count($fields)) {
$keys = array_keys($fields);
$values = '';
$x = 1;
foreach ($fields as $field) {
$values .= '?';
if($x < count($fields)) {
$values .= ', ';
}
$x++;
}
$sql = "INSERT INTO {$table} (`" . implode('` , `', $keys) . "`) VALUES({$values})";
if (!$this->query($sql, $fields)->error()) {
return true;
}
}
return false;
}
public function update($table, $where, $parametar, $fields) {
$set = '';
$x = 1;
foreach ($fields as $name => $value) {
$set .= "{$name} = ?";
if ($x < count($fields)) {
$set .= ', ';
}
$x++;
}
if (is_int($parametar)) {
$sql = "UPDATE {$table} SET {$set} WHERE {$where} = {$parametar}";
} else {
$sql = "UPDATE {$table} SET {$set} WHERE {$where} = '{$parametar}'";
}
if (!$this->query($sql, $fields)->error()) {
return true;
}
return false;
}
public function results() {
return $this->_results;
}
public function error() {
return $this->_error;
}
public function count() {
return $this->_count;
}
}
Then you can query database like DB::getInstance()->getAll('tableName')->results();. Change DB credentials in __construct, or watch his videos (which I recomend).

OOP Object of class __PHP_Incomplete_Class

hello i have this code for login and register in PHP OOP
<?php
class DB {
public static $instance = null;
private $_pdo = null,
$_query = null,
$_error = false,
$_results = null,
$_count = 0;
private function __construct() {
try {
$this->_pdo = new PDO('mysql:host=' . Config::get('mysql/host') . ';dbname=' . Config::get('mysql/db'), Config::get('mysql/username'), Config::get('mysql/password'), array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
//$this->query('SET NAMES utf8');
} catch(PDOExeption $e) {
die($e->getMessage());
}
}
public static function getInstance() {
if(!isset(self::$instance)) {
self::$instance = new DB();
}
return self::$instance;
}
public function query($sql, $params = array()) {
$this->_error = false;
if($this->_query = $this->_pdo->prepare($sql)) {
$x = 1;
if(count($params)) {
foreach($params as $param) {
$this->_query->bindValue($x, $param);
$x++;
}
}
if($this->_query->execute()) {
$this->_results = $this->_query->fetchAll(PDO::FETCH_OBJ);
$this->_count = $this->_query->rowCount();
} else {
$this->_error = true;
}
}
return $this;
}
public function get($table, $where) {
return $this->action('SELECT *', $table, $where);
}
public function delete($table, $where) {
return $this->action('DELETE', $table, $where);
}
public function action($action, $table, $where = array()) {
if(count($where) === 3) {
$operators = array('=', '>', '<', '>=', '<=');
$field = $where[0];
$operator = $where[1];
$value = $where[2];
if(in_array($operator, $operators)) {
$sql = "{$action} FROM {$table} WHERE {$field} {$operator} ?";
if(!$this->query($sql, array($value))->error()) {
return $this;
}
}
return false;
}
}
public function insert($table, $fields = array()) {
$keys = array_keys($fields);
$values = null;
$x = 1;
foreach($fields as $value) {
$values .= "?";
if($x < count($fields)) {
$values .= ', ';
}
$x++;
}
$sql = "INSERT INTO {$table} (`" . implode('`, `', $keys) . "`) VALUES ({$values})";
if(!$this->query($sql, $fields)->error()) {
return true;
}
return false;
}
public function update($table, $id, $fields = array()) {
$set = null;
$x = 1;
foreach($fields as $name => $value) {
$set .= "{$name} = ?";
if($x < count($fields)) {
$set .= ', ';
}
$x++;
}
$sql = "UPDATE {$table} SET {$set} WHERE id = {$id}";
if(!$this->query($sql, $fields)->error()) {
return true;
}
return false;
}
public function results() {
// Return result object
return $this->_results;
}
public function first() {
return $this->_results[0];
}
public function count() {
// Return count
return $this->_count;
}
public function error() {
return $this->_error;
}
public function lastInsertId(){
return $this->_pdo->lastInsertId();
}
public function __sleep(){
return array();
}
}
and i save the user login data in session, everything works fine in localhost, but in my web server i have a problem in the line $this->_query->bindValue($x, $param);
Catchable fatal error: Object of class __PHP_Incomplete_Class could not be converted to string in
i know that is something wrong with my session but i cant find whats the problem, and this is my session class
<?php
class Session {
public static function exists($name) {
return (isset($_SESSION[$name])) ? true : false;
}
public static function get($name) {
return $_SESSION[$name];
}
public static function put($name, $value) {
return $_SESSION[$name] = $value;
}
public static function delete($name) {
if(self::exists($name)) {
unset($_SESSION[$name]);
}
}
public static function flash($name, $string = null) {
if(self::exists($name)) {
$session = self::get($name);
self::delete($name);
return $session;
} else if ($string) {
self::put($name, $string);
}
}
}
please tell me if you can what i can to do with that error thank you very much.
and this is the code where i store my session
public function login($username = null, $password = null, $remember = false) {
if(!$username && !$password && $this->exists()) {
Session::put($this->_sessionName, $this->data()->id);
} else {
$user = $this->find($username);
if($user) {
if($this->data()->password === Hash::make($password, $this->data()->salt)) {
Session::put($this->_sessionName, $this->data()->id);
if($remember) {
$hash = Hash::unique();
$hashCheck = $this->_db->get(Config::get('mysql/tbl_user_sessions'), array('user_id', '=', $this->data()->id));
if(!$hashCheck->count()) {
$this->_db->insert(Config::get('mysql/tbl_user_sessions'), array(
'user_id' => $this->data()->id,
'hash' => $hash
));
} else {
$hash = $hashCheck->first()->hash;
}
Cookie::put($this->_cookieName, $hash, Config::get('remember/cookie_expiry'));
}
return true;
}
}
}
return false;
}
i just do var_dump of my session['user']
array(1) {
["user"]=> &object(__PHP_Incomplete_Class)#1 (6) {
["__PHP_Incomplete_Class_Name"]=> string(4) "User"
["_db":"User":private]=> object(__PHP_Incomplete_Class)#2 (1) {
["__PHP_Incomplete_Class_Name"]=> string(2) "DB"
}
["_sessionName":"User":private]=> string(4) "user"
["_cookieName":"User":private]=> string(4) "hash"
["_data":"User":private]=> object(stdClass)#3 (7) {
["id"]=> string(3) "144"
["username"]=> string(5) "admin"
["password"]=> string(64) "0611affa6664e471b939cd3197b49e0c3b47d146fc12a472c4275dbd85a7cd67"
["salt"]=> string(32) "458a0dbfbd9bdca381e50b8d753329ea"
["name"]=> string(12) "Artur Papyan"
["joined"]=> string(19) "2013-11-29 07:41:54"
["group"]=> string(1) "1"
}
["_isLoggedIn":"User":private]=> bool(true)
}
}
It looks as though you are attempting to output the result to a string, which it cannot do as the class returns an object.
Also, Referring to DB.php line 38 is a red herring, as it is displaying the error message to the called function.
$this->_query->bindValue($x, $param);
Instead look to where your code is calling the class, and how you are attempting to bind your variables within your code it's self, instead of the actual call to DB.php file.

Multiple Tables in Zend based on the Quick Start Guide Example

I am new to Zend and have been attempting to follow the Zend Quick Start Guide's example of using Data Mappers and extending Zend_Db_Table_Abstract. I think I've grasped the general concepts, but I am now wondering, how I would go about modifying the guide's example code to allow for multiple tables.
Here is the part of the code I am currently interested in modifying:
protected $_dbTable;
public function setDbTable($dbTable)
{
if (is_string($dbTable)) {
$dbTable = new $dbTable();
}
if (!$dbTable instanceof Zend_Db_Table_Abstract) {
throw new Exception('Invalid table data gateway provided');
}
$this->_dbTable = $dbTable;
return $this;
}
public function getDbTable()
{
if (null === $this->_dbTable) {
$this->setDbTable('Application_Model_DbTable_Guestbook');
}
return $this->_dbTable;
}
I have changed it to this:
protected $_dbTables;
public function setDbTable($dbTable, $tableName)
{
if (is_string($dbTable)) {
$dbTable = new $dbTable();
}
if (!$dbTable instanceof Zend_Db_Table_Abstract) {
throw new Exception('Invalid table data gateway provided');
}
$this->_dbTables[$tableName] = $dbTable;
return $this;
}
public function getDbTables()
{
if (null === $this->_dbTables) {
$this->setDbTable('Application_Model_DbTable_Courses', 'courses');
$this->setDbTable('Application_Model_DbTable_CourseTimes', 'course_times');
}
return $this->_dbTables;
}
Is this a correct way to go about implementing multiple tables within the Data Mapper pattern or would you do it differently? Thanks for your help in advance!
Assuming that you want to return data from related tables, you should either add queries with joins to one Zend_Db_Table or use Zend_Db_Table_Relationships to fetch associated data. The benefit of using Joins is that you will only do one query over many when using Relationships. The drawback is that joined tables wont return Zend_Db_Table_Row objects (iirc), but since you are going to map them onto your Domain objects anyway, it's not that much of an issue.
Structurally, you can do like I suggested in How to change Zend_Db_Table name within a Model to insert in multiple tables. Whether you create a Gateway of Gateways or simply aggregate the Table Gateways in the DataMapper directly is really up to you. Just compose them as you see fit.
I'm not sure if it's the best practice, but, this is my abstract mapper:
<?php
abstract class Zf_Model_DbTable_Mapper
{
protected $_db;
protected $_dbTable = null;
protected $_systemLogger = null;
protected $_userLogger = null;
public function __construct()
{
$this->_systemLogger = Zend_Registry::get('systemLogger');
$this->_userLogger = Zend_Registry::get('userLogger');
// Set the adapter
if(null !== $this->_dbTable)
{
$tableName = $this->_dbTable;
$this->_db = $this->$tableName->getAdapter();
}
}
public function __get($value)
{
if(isset($this->$value))
{
return $this->$value;
}
$dbTable = 'Model_DbTable_' . $value;
$mapper = 'Model_' . $value;
if(class_exists($dbTable))
{
return new $dbTable;
}
elseif(class_exists($mapper))
{
return new $mapper;
}
else
{
throw new Exception("The property, DbTable or Mapper \"$value\" doesn't exists");
}
}
public function __set($key,$value)
{
$this->$key = $value;
}
public function getById($id)
{
$resource = $this->getDefaultResource();
$id = (int)$id;
$row = $resource->fetchRow('id =' . $id);
if (!$row) {
throw new Exception("Count not find row $id");
}
return $row;
}
public function getAll()
{
$resource = $this->getDefaultResource();
return $resource->fetchAll()->toArray();
}
public function save(Zf_Model $Model)
{
$dbTable = $this->getDefaultResource();
$data = $Model->toArray();
if(false === $data) return false;
if(false === $Model->isNew())
{
if(1 == $dbTable->update($data, 'id =' . (int)$Model->getId()))
{
return $Model;
}
}
else
{
$id = $dbTable->insert($data);
if($id)
{
$Model->setId($id);
return $Model;
}
}
return false;
}
public function remove($id)
{
return $this->getDefaultResource()->delete('id =' . (int) $id);
}
protected function getDefaultResource()
{
if(empty($this->_dbTable))
{
throw new Exception('The $_dbTable property was not set.');
}
$classname = 'Model_DbTable_' . $this->_dbTable;
if(!class_exists($classname))
{
throw new Exception("The Model_DbTable_\"$classname\" class was not found.");
}
return new $classname;
}
protected function getDefaultModel()
{
return current($this->_models);
}
protected function getResources()
{
return $this->_resources;
}
}
And this is one for my implemented mappers:
<?php
class Model_TwitterPostsMapper extends Zf_Model_DbTable_Mapper
{
/*
* Data Source
* #var string Zend_Db_Table name
*/
protected $_dbTable = 'TwitterPosts';
public function recordExists($Item)
{
$row = $this->TwitterPosts->fetchRow($this->TwitterPosts->select()->where('status_id =?', $Item->getSource()->getStatusId()));
if($row)
{
return $row->id;
}
return false;
}
public function getLastUpdate($options)
{
$select = $this->TwitterPosts->select()
->setIntegrityCheck(false)
->from(array('t' => 'twt_tweets'), 't.created_at')
->join(array('u' => 'twt_users'), 't.user_id = u.id', '')
->order('t.created_at DESC');
if($options['user_id'])
{
$select->where("t.user_id = ?", $options['user_id']);
}
if($options['terms'])
{
if(is_array($options['terms']))
{
$condition = '';
foreach($options['terms'] as $i => $term)
{
$condition .= ($i > 0) ? ' OR ' : '';
$condition .= $this->getAdapter()->quoteInto('content LIKE ?',"%$term%");
}
if($condition)
{
$select->where($condition);
}
}
}
return $this->TwitterPosts->fetchRow($select)->created_at;
}
public function getSinceId($term = null)
{
$select = $this->TwitterPosts->select()->setIntegrityCheck(false)
->from('twt_tweets_content', 'status_id')
->where('MATCH(content) AGAINST(? IN BOOLEAN MODE)', "$term")
->order('status_id ASC')
->limit(1);
//echo $select; exit;
$tweet = $this->TwitterPosts->fetchRow($select);
if(null !== $tweet) return $tweet->status_id;
return 0;
}
public function getAllByStatusId($statuses_id)
{
$select = $this->TwitterPosts->select()
->setIntegrityCheck(false)
->from(array('t' => 'twt_tweets'), array('t.id', 't.user_id', 't.status_id','t.user_id'))
->join(array('u' => 'twt_users'), 't.user_id = u.id', array('u.screen_name', 'u.profile_image'))
->where('status_id IN(?)', $statuses_id);
$rows = $this->TwitterPosts->fetchAll($select);
$Posts = array();
foreach($rows as $row)
{
// Here we populate the models only with the specific method return data
$data = $row->toArray();
$Post = new Model_TwitterPost($data['id']);
$Post->populate($data);
$User = new Model_TwitterUser($data['user_id']);
$User->populate($data);
$Post->setUser($User);
$Posts[] = $Post;
}
return $Posts;
}
public function getAllSince($since_id)
{
$select = $this->TwitterPosts->select()
->setIntegrityCheck(false)
->from(array('t' => 'twt_tweets'), array('t.status_id','t.user_id'))
->join(array('u' => 'twt_users'), 't.user_id = u.id', array('u.screen_name', 'u.profile_image'))
->where('status_id > ?', $since_id)
->order('t.datetime DESC');
$rows = $this->TwitterPosts->fetchAll($select);
$Posts = array();
foreach($rows as $row)
{
// Here we populate the models only with the specific method return data
// TODO: This is not a truly lazy instatiation, since there's no way to get the not setted properties
$data = $row->toArray();
$Post = new Model_TwitterPost($data);
$User = new Model_TwitterUser($data);
$Post->setUser($User);
$Posts[] = $Post;
}
return $Posts;
}
public function getTotalRatedItems($options)
{
$options = $this->prepareOptions($options);
$select = $this->TwitterPosts->select()
->setIntegrityCheck(false)
->from(array('t' => 'twt_tweets'), array('COUNT(DISTINCT t.id) AS total','r.rate'))
->join(array('u' => 'twt_users'), 't.user_id = u.id', '')
->join(array('r' => 'twt_tweets_rate'), 't.id = r.tweet_id', array('r.rate'))
->group('r.rate')
->order('t.datetime DESC');
$select = $this->prepareSelect($select, $options);
$rates = $this->TwitterPosts->fetchAll($select)->toArray();
$itemsRated = array('Green' => 0, 'Yellow' => 0, 'Orange' => 0, 'Red' => 0, 'Gray' => 0);
foreach ($rates as $rate)
{
$itemsRated[$rate['rate']] = $rate['total'];
}
return $itemsRated;
}
public function getUsersActivity($options)
{
$options = $this->prepareOptions($options);
$select = $this->TwitterPosts->select()
->setIntegrityCheck(false)
->from(array('t' => 'twt_tweets'), array('COUNT(DISTINCT t.id) AS total','DATE(t.datetime) AS datetime'))
->join(array('u' => 'twt_users'), 't.user_id = u.id', '')
->joinLeft(array('r' => 'twt_tweets_rate'), 't.id = r.tweet_id', '')
->group('t.user_id')
->order('t.datetime DESC');
$select = $this->prepareSelect($select, $options);
$activity = $this->TwitterPosts->fetchAll($select)->toArray();
return $activity;
}
public static function prepareOptions($options)
{
if(!is_array($options))
{
$options = array();
}
date_default_timezone_set('America/Sao_Paulo');
if(Zend_Date::isDate($options['start_date']))
{
$date = new Zend_Date($options['start_date']);
$date->setTime('00:00:00');
$date->setTimezone('UTC');
$options['start_date'] = $date->toString('yyyy-MM-dd HH:mm:ss');
}
if(Zend_Date::isDate($options['end_date']))
{
$date = new Zend_Date($options['end_date']);
$date->setTime('23:59:59');
$date->setTimezone('UTC');
$options['end_date'] = $date->toString('yyyy-MM-dd HH:mm:ss');
}
date_default_timezone_set('UTC');
$options['mainTerms'] = array();
if(!empty($options['terms']) && !is_array($options['terms']))
{
$options['mainTerms'] = explode(' ', $options['terms']);
}
if(!is_array($options['terms']))
{
$options['terms'] = array();
}
if($options['group_id'] || $options['client_id'])
{
$TwitterSearches = new Model_DbTable_TwitterSearches();
$options['terms'] = array_merge($TwitterSearches->getList($options),$options['terms']);
if(empty($options['terms']))
{
$options['terms'] = array();
}
}
return $options;
}
public static function prepareSelect($select, $options)
{
if($options['start_date'])
{
$select->where('t.datetime >= ?', $options['start_date']);
}
if($options['end_date'])
{
$select->where('t.datetime <= ?', $options['end_date']);
}
foreach($options['mainTerms'] as $mainTerm)
{
$select->where('t.content LIKE ?', "%$mainTerm%");
}
if($options['user_id'])
{
$select->where("t.user_id = ?", $options['user_id']);
}
if($options['terms'])
{
$select->where('MATCH (t.content) AGASINT(?)', $options['terms']);
}
if($options['rate'])
{
if($options['rate'] == 'NotRated')
{
$select->where('r.rate IS NULL');
}
else
{
$select->where('r.rate = ?', $options['rate']);
}
}
if($options['last_update'])
{
$select->where('t.created_at > ?', $options['last_update']);
}
if($options['max_datetime'])
{
$select->where('t.created_at < ?', $options['max_datetime']);
}
return $select;
}
}
The Model:
<?php
class Model_TwitterPost extends Zf_Model
{
private $_name = 'twitter';
protected $_properties = array(
'id',
'status_id',
'user_id',
'content'
);
protected $_User = null;
public function setUser(Zf_Model $User)
{
$this->_User = $User;
}
public function getUser()
{
return $this->_User;
}
public function getPermalink()
{
return 'http://twitter.com/' . $this->screen_name . '/' . $this->status_id;
}
public function hasTerm($term)
{
if(preg_match("/\b$term\b/i", $this->getContent()))
{
return true;
}
return false;
}
public function getEntityName()
{
return $this->_name;
}
public function getUserProfileLink()
{
return $this->getUser()->getProfileLink() . '/status/' . $this->getStatusId();
}
}
Abstract model (Generic Object):
<?php
abstract class Zf_Model
{
protected $_properties = array();
protected $_modified = array();
protected $_data = array();
protected $_new = true;
protected $_loaded = false;
public function __construct($id=false)
{
$id = (int)$id;
if(!empty($id))
{
$this->_data['id'] = (int)$id;
$this->setNew(false);
}
}
public function populate($data)
{
if(is_array($data) && count($data))
{
foreach($data as $k => $v)
{
if(in_array($k,$this->_properties))
{
$this->_data[$k] = $v;
}
}
}
$this->setLoaded(true);
}
public function setNew($new=true)
{
$this->_new = (bool)$new;
}
public function isNew()
{
return $this->_new;
}
public function setLoaded($loaded = true)
{
$this->_loaded = (bool)$loaded;
}
public function isLoaded()
{
return $this->_loaded;
}
public function __call($methodName, $args) {
if(method_exists($this, $methodName))
{
return $this->$methodName($args);
}
$property = $methodName;
if (preg_match('~^(set|get)(.*)$~', $methodName, $matches))
{
$filter = new Zend_Filter_Word_CamelCaseToUnderscore();
$property = strtolower($filter->filter($matches[2]));
if(in_array($property, $this->_properties))
{
if('set' == $matches[1])
{
$this->_data[$property] = $args[0];
if(true === $this->isLoaded())
{
$this->_modified[$property] = true;
}
return $this;
}
elseif('get' == $matches[1])
{
if(array_key_exists($property, $this->_data))
{
return $this->_data[$property];
}
throw new Exception("The property $property or $methodName() method was not setted for " . get_class($this));
}
}
}
throw new Exception("The property '$property' doesn't exists.");
}
public function __get($key)
{
if(isset($this->_data[$key]))
{
return $this->_data[$key];
}
return $this->$key;
}
public function __set($key,$value)
{
if(array_key_exists($key,$this->_properties))
{
$this->_data[$key] = $value;
return;
}
$this->$key = $value;
}
public function getId()
{
return (!$this->_data['id']) ? null : $this->_data['id'];
}
public function toArray()
{
// If it's a new object
if(true === $this->isNew())
{
return $this->_data;
}
// Else, if it's existing object
$data = array();
foreach($this->_modified as $k=>$v)
{
if($v)
{
$data[$k] = $this->_data[$k];
}
}
if(count($data))
{
return $data;
}
return false;
}
public function reload()
{
$this->_modified = array();
}
}

Categories