I am getting an warning message in my website :
Warning: Creating default object from empty value in /homepages/16/d381040813/htdocs/components/com_events_booking/models/event.php on line 169
I have opened 169 th line in event.php , the code is:
$row->event->BeforeDisplay = trim(implode("\n", $results));
My event.php file is :
<?php
// no direct access
defined('_JEXEC') or die('Restricted access');
jimport('joomla.application.component.model');
/**
* Event_Booking Component - Event Model
* #package Event_Booking
* #subpackage Model
*/
class Events_BookingModelEvent extends JModel {
/** #var int Total of documents */
var $_total = null;
/** #var object Pagination object */
var $_pagination = null;
/** #var string */
var $nullDate = null;
/** #var string */
var $now = null;
/** #var int */
var $_id = null;
/** #var array realty data */
var $_data = null;
/**
* Constructor.
*/
function __construct() {
global $mainframe, $option;
parent::__construct();
$this->nullDate = $this->_db->getNullDate();
$jdate = JFactory::getDate();
$this->now = $jdate->toMySQL();
$id = JRequest::getInt('id', 0);
if( !$id ){
$session = &JFactory::getSession();
$user = JFactory::getUser();
$joinevent = $session->get('joinevent');
if( #count( $joinevent[$user->get('id')] ) ){
$id = $joinevent[$user->get('id')]['id'];
}
}
$this->setId($id);
}
/**
* Method to set the identifier
*
* #access public
* #param int $id realty identifier
*/
function setId($id) {
$this->_id = $id;
$this->_data = null;
}
/**
* Method to get event item data.
* #access public
* #return object
*/
function getData($id = null) {
// Lets load the data if it doesn't already exist
if (empty($this->_data)) {
$id = $id ? $id : $this->_id;
$query = 'SELECT a.*, b.id AS bid , b.title AS btitle , c.id AS cid , c.title AS ctitle, c.street, c.city, c.state, c.zip, ct.name AS `country`, c.website, c.latitude, c.longitude'
. ' FROM #__events_events AS a'
. ' INNER JOIN #__events_categories AS b ON a.category_id = b.id'
. ' INNER JOIN #__events_venues AS c ON a.venue_id = c.id'
. ' LEFT JOIN #__events_countries AS ct ON ct.code LIKE c.country'
. ' WHERE a.published = 1 AND a.id = '. (int) $id
;
$this->_db->setQuery($query);
$this->_data = $this->_db->loadObject();
}
if (!$this->_data->id) {
JError::raiseError( 404, JText::_('COM_EVENTS_BOOKING_EVENT_NOT_FOUND'));
}
return $this->_data;
}
/**
* Method to get group item data.
* #access public
* #return array
*/
function getGroup($id = null) {
$query = 'SELECT g.* FROM #__events_groups as g'
. ' WHERE g.published = 1 AND a.id = '. (int) $id
;
$this->_db->setQuery($query);
$row = $this->_db->loadObject();
return $row;
}
/**
* Method to get Expired item data.
* #access public
* #return array
*/
function getExpired() {
$date = new JDate();
$id = $this->_id;
$query = 'SELECT COUNT(*) FROM #__events_events AS a WHERE a.id = '.(int)$id.' AND a.published = 1 AND a.expired > '.$this->_db->Quote($date->toMySQL());
$this->_db->setQuery($query);
$row = $this->_db->loadResult();
return $row;
}
/**
* Method to get Expired Capacity.
* #access public
* #return array
*/
function getCapacity() {
$data = $this->getData();
$id = $this->_id;
$result = $this->getMemberCapacity($data->capacity);
return $result;
}
/**
* Method to get Member Capacity COM_EVENTS_BOOKING_JOIN_EVENT.
* #access public
* #return array
*/
function getMemberCapacity($capacity=0) {
$id = $this->_id;
// $query = 'SELECT COUNT(*) FROM #__events_members AS a WHERE a.event_id = '.(int)$id;
$query = 'SELECT COUNT(*)'
. ' FROM #__events_members AS a'
. ' INNER JOIN #__events_bookings AS b ON b.id = a.booking_id'
. ' WHERE a.event_id = '.(int)$id
. ' AND b.approved = 1'
. ' AND a.approved = 1'
;
$this->_db->setQuery($query);
$row = $this->_db->loadResult();
$result = array();
$result['m_capacity'] = $row;
$result['e_capacity'] = (int)($capacity-(int)$row);
return $result;
}
function execPlugins(&$row, $view, $task){
$params = &JComponentHelper::getParams('com_events_booking');
$limitstart = JRequest::getInt('limitstart');
//Import plugins
$dispatcher = &JDispatcher::getInstance();
JPluginHelper::importPlugin ('content');
$row->text = $row->description;
$results = $dispatcher->trigger('onBeforeDisplay', array ( 'com_events_booking.event', &$row, &$params, $limitstart));
$row->event->BeforeDisplay = trim(implode("\n", $results));
$results = $dispatcher->trigger('onAfterDisplay', array ( 'com_events_booking.event', &$row, &$params, $limitstart));
$row->event->AfterDisplay = trim(implode("\n", $results));
$results = $dispatcher->trigger('onAfterDisplayTitle', array ( 'com_events_booking.event', &$row, &$params, $limitstart));
$row->event->AfterDisplayTitle = trim(implode("\n", $results));
$results = $dispatcher->trigger('onBeforeDisplayContent', array ( 'com_events_booking.event', &$row, &$params, $limitstart));
$row->event->BeforeDisplayContent = trim(implode("\n", $results));
$results = $dispatcher->trigger('onAfterDisplayContent', array ( 'com_events_booking.event', &$row, &$params, $limitstart));
$row->event->AfterDisplayContent = trim(implode("\n", $results));
$dispatcher->trigger('onContentPrepare', array ( 'com_events_booking.event', &$row, &$params, $limitstart));
return $row;
}
/**
* Method to store the booking
*
* #access public
* #param $data
* #return boolean True on success
*/
function store($data) {
//$row = JTable::getInstance('form', 'Table');
$row =& $this->getTable('event');
// bind the form fields to the version table
if (!$row->bind($data)) {
$this->setError($this->_db->getErrorMsg());
return false;
}
// make sure the category is valid
if (!$row->check()) {
$this->setError($this->_db->getErrorMsg());
return false;
}
// store the category table to the database
if (!$row->store()) {
$this->setError($this->_db->getErrorMsg());
return false;
}
return $row;
}
I googled a day, but no result. Please help me to hide this warning or solving this.
the page url in which error shown:
http://www.lefoodist.biz/calendar-gb/calendar-hosted-gastronomic-dinners?view=event&id=478#box-details
Nowhere have you created a $row->event object. So when you set $row->event->BeforeDisplay, the BeforeDisplay object is being created against something that hasn't been set. If you do something like $row->event = new stdClass(); before your events start to run, this should clear it up.
Related
I am working on a code where everything works perfectly fine but the issue I am currently facing is that I want to make it more generic by separating my BaseModel Class from my DbOPerations Class.
At the moment, I am doing this :
Basemodel.php :
class BaseModel extends DbOperatons implements ModelInterface{
protected $table = 'default';
protected $db;
private $OperateOnDb=new DbOperations();
/**
* Fetch all records from table
* #return array
*/
public function all(){
return $this->db->select(["*"])->run();
}
/**
* Find record by given id
* #param int $id
* #return array
*/
public function find($id = 1){
return $this->db->select(["*"])->where(['id'=>$id])->run();
}
}
DbOperations.php:
<?php
namespace core\others;
use PDO;
class DbOperations {
protected $pdo;
protected $selectParams = '*';
protected $table = '';
protected $lastTable = '';
protected $groupByColumns = '';
protected $where = '';
/**
* DbOperations constructor.
* #param PDO $pdo
*/
public function __construct(PDO $pdo) {
$this->pdo= $pdo;
}
/**
* Setting select parameters
* #param $params
* #return $this
*/
public function select($params){
$this->selectParams = implode(",",$params);
return $this;
}
/**
* Set table name as array like ["students","teachers"]
* #param array $table
* #return $this
*/
public function setTable($table = []){
$this->lastTable = $this->table;
$this->table = $table;
if(is_array($table)) {
$tableNames = '';
foreach($table as $table)
$tableNames .= $table . ", ";
$this->table = rtrim($tableNames, ", ");
}
return $this;
}
/**
* Setting group by clause
* #param array $columns
* #return $this
*/
public function groupBy($columns = []){
$this->groupByColumns = implode(", ", $columns);
return $this;
}
/**
* Setting Where clause
* #param array $whereClause
* #param string $operator
* #param string $operand
* #return $this
*/
public function where($whereClause = [],$operator = '=', $operand = 'AND'){
$where = [];
foreach ($whereClause as $column => $data)
$where[] = $column . $operator . $data;
$where = implode(' ' . $operand . ' ', $where);
$this->where = $where;
return $this;
}
/**
* Generate dynamic SQL
* #return string
*/
public function generateSQL(){
$query = "SELECT {$this->selectParams} FROM {$this->table}";
if ($this->where!='')
$query .= " WHERE " . $this->where;
if ($this->groupByColumns!='')
$query .= " GROUP BY " . $this->groupByColumns;
return $query;
}
/**
* Returns a result of dynamic query made by select, where, group by functions
* #return array
*/
public function run(){
$query = $this->generateSQL();
$statement = $this->pdo->prepare($query);
$statement->execute();
$this->table = $this->lastTable;
return $statement->fetchAll(2);
}
/**
* For creating record in DB with key value pair
* #param array $data
* #param null $table
* #return integer Last Inserted id
*/
public function create($data = ['key'=>'value'],$table = null){
$this->lastTable = $this->table;
$table = (isset($table)?$table : $this->table);
$columns = '';
$values= '';
foreach($data as $key => $valuePair){
$columns .= "{$key},";
$values .= "?,";
}
$columns = substr($columns, 0, -1);
$values = substr($values, 0, -1);
$query = "INSERT INTO {$table} ({$columns}) VALUES ({$values})";
$this->pdo->prepare($query)->execute(array_values($data));
return $this->pdo->lastInsertId();
}
// #codeCoverageIgnoreStart
/**
* For updating record in database table
* #param array $data
* #return $this
*/
public function update($data = []){
$columns = '';
foreach($data as $key => $valuePair){
if($key!='id')
$columns .= "{$key}=?, ";
}
$columns = substr($columns, 0, -2);
$query = "UPDATE {$this->table} SET {$columns} WHERE id=?";
$this->pdo->prepare($query)->execute(array_values($data));
return $this;
}
/**
* For deleting record in database table
* #param $id
* #param null $table
* #return $this
*/
public function delete($id,$table = null){
$this->lastTable = $this->table;
$table = (isset($table)?$table : $this->table);
$query = "DELETE FROM {$table} WHERE id =:id";
$statement = $this->pdo->prepare( $query);
$statement->bindParam(':id', $id);
$statement->execute();
return $this;
}
}
Now I want to use the functionalities of my DBOPerations to work in basemodel class without extending. I have tried declaring a dboperation object in basemodel class but I m not able to do that, please help!
I have this code, can insert an array at first time, but when trying replace and update, it returns error
Integrity constraint violation: 1062 Duplicate entry for composite key ['periodo_id','asociado_id']
Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Lectura extends Model
{
protected $primaryKey = array('periodo_id', 'asociado_id');
public $timestamps = false;
public $incrementing = false;
}
And controller:
$rows = DB::table('lecturas_temp')->get();
$arr = array();
foreach ($rows as $row) {
$a = [
'asociado_id' => $row->asociado_id,
'periodo_id' => $request->periodo_id,
'nombre' => $row->nombre,
];
array_push($arr, $a);
}
DB::table('lecturas')->insert($arr);
Any alternatives to line DB::table('lecturas')->insert($arr)?
I tried Eloquent Lectura::insert($arr) and updateOrCreate but same results;
The error message is crystal clear. You are using a composite key ['periodo_id', 'asociado_id'], which means you cannot insert the same data twice because you defined it as your primary key.
If you are expecting to have duplicate composite key, then please, remove it from your model as the primary key. However, if you want the data to be unique, you should use updateOrCreate().
$rows = DB::table('lecturas_temp')->get();
$arr = array();
foreach ($rows as $row) {
Lectura::updateOrCreate(
[
'asociado_id' => $row->asociado_id,
'periodo_id' => $request->periodo_id
],
[
'nombre' => $row->nombre
]
);
}
As you can see, updateOrCreate() takes 2 arrays as arguments. The first array is the elements used to verify if it already exists. Also, unfortunately you will have to do it one by one instead all in one go as you were doing.
If you want to stick to the query builder, you can use DB::updateOrInsert(), with the same call signature (passing 2 arrays).
Using this code I did solved: https://github.com/yadakhov/insert-on-duplicate-key
class Lectura extends Model
{
protected $primaryKey = array('periodo_id', 'asociado_id');
public $timestamps = false;
public $incrementing = false;
public static function insertOnDuplicateKey(array $data, array $updateColumns = null)
{
if (empty($data)) {
return false;
}
// Case where $data is not an array of arrays.
if (!isset($data[0])) {
$data = [$data];
}
$sql = static::buildInsertOnDuplicateSql($data, $updateColumns);
$data = static::inLineArray($data);
return self::getModelConnectionName()->affectingStatement($sql, $data);
}
/**
* Insert using mysql INSERT IGNORE INTO.
*
* #param array $data
*
* #return int 0 if row is ignored, 1 if row is inserted
*/
public static function insertIgnore(array $data)
{
if (empty($data)) {
return false;
}
// Case where $data is not an array of arrays.
if (!isset($data[0])) {
$data = [$data];
}
$sql = static::buildInsertIgnoreSql($data);
$data = static::inLineArray($data);
return self::getModelConnectionName()->affectingStatement($sql, $data);
}
/**
* Insert using mysql REPLACE INTO.
*
* #param array $data
*
* #return int 1 if row is inserted without replacements, greater than 1 if rows were replaced
*/
public static function replace(array $data)
{
if (empty($data)) {
return false;
}
// Case where $data is not an array of arrays.
if (!isset($data[0])) {
$data = [$data];
}
$sql = static::buildReplaceSql($data);
$data = static::inLineArray($data);
return self::getModelConnectionName()->affectingStatement($sql, $data);
}
/**
* Static function for getting table name.
*
* #return string
*/
public static function getTableName()
{
$class = get_called_class();
return (new $class())->getTable();
}
/**
* Static function for getting connection name
*
* #return string
*/
public static function getModelConnectionName()
{
$class = get_called_class();
return (new $class())->getConnection();
}
/**
* Get the table prefix.
*
* #return string
*/
public static function getTablePrefix()
{
return self::getModelConnectionName()->getTablePrefix();
}
/**
* Static function for getting the primary key.
*
* #return string
*/
public static function getPrimaryKey()
{
$class = get_called_class();
return (new $class())->getKeyName();
}
/**
* Build the question mark placeholder. Helper function for insertOnDuplicateKeyUpdate().
* Helper function for insertOnDuplicateKeyUpdate().
*
* #param $data
*
* #return string
*/
protected static function buildQuestionMarks($data)
{
$lines = [];
foreach ($data as $row) {
$count = count($row);
$questions = [];
for ($i = 0; $i < $count; ++$i) {
$questions[] = '?';
}
$lines[] = '(' . implode(',', $questions) . ')';
}
return implode(', ', $lines);
}
/**
* Get the first row of the $data array.
*
* #param array $data
*
* #return mixed
*/
protected static function getFirstRow(array $data)
{
if (empty($data)) {
throw new \InvalidArgumentException('Empty data.');
}
list($first) = $data;
if (!is_array($first)) {
throw new \InvalidArgumentException('$data is not an array of array.');
}
return $first;
}
/**
* Build a value list.
*
* #param array $first
*
* #return string
*/
protected static function getColumnList(array $first)
{
if (empty($first)) {
throw new \InvalidArgumentException('Empty array.');
}
return '`' . implode('`,`', array_keys($first)) . '`';
}
/**
* Build a value list.
*
* #param array $first
*
* #return string
*/
protected static function buildValuesList(array $first)
{
$out = [];
foreach (array_keys($first) as $key) {
$out[] = sprintf('`%s` = VALUES(`%s`)', $key, $key);
}
return implode(', ', $out);
}
/**
* Inline a multiple dimensions array.
*
* #param $data
*
* #return array
*/
protected static function inLineArray(array $data)
{
return call_user_func_array('array_merge', array_map('array_values', $data));
}
/**
* Build the INSERT ON DUPLICATE KEY sql statement.
*
* #param array $data
* #param array $updateColumns
*
* #return string
*/
protected static function buildInsertOnDuplicateSql(array $data, array $updateColumns = null)
{
$first = static::getFirstRow($data);
$sql = 'INSERT INTO `' . static::getTablePrefix() . static::getTableName() . '`(' . static::getColumnList($first) . ') VALUES' . PHP_EOL;
$sql .= static::buildQuestionMarks($data) . PHP_EOL;
$sql .= 'ON DUPLICATE KEY UPDATE ';
if (empty($updateColumns)) {
$sql .= static::buildValuesList($first);
} else {
$sql .= static::buildValuesList(array_combine($updateColumns, $updateColumns));
}
return $sql;
}
/**
* Build the INSERT IGNORE sql statement.
*
* #param array $data
*
* #return string
*/
protected static function buildInsertIgnoreSql(array $data)
{
$first = static::getFirstRow($data);
$sql = 'INSERT IGNORE INTO `' . static::getTablePrefix() . static::getTableName() . '`(' . static::getColumnList($first) . ') VALUES' . PHP_EOL;
$sql .= static::buildQuestionMarks($data);
return $sql;
}
/**
* Build REPLACE sql statement.
*
* #param array $data
*
* #return string
*/
protected static function buildReplaceSql(array $data)
{
$first = static::getFirstRow($data);
$sql = 'REPLACE INTO `' . static::getTablePrefix() . static::getTableName() . '`(' . static::getColumnList($first) . ') VALUES' . PHP_EOL;
$sql .= static::buildQuestionMarks($data);
return $sql;
}
In Controller
Lectura::replace($arr);
* #author Josh Campbell
* #author Alexander V. Butenko
* #copyright Copyright (c) 2010
* #license http://opensource.org/licenses/gpl-3.0.html GNU Public License
* #version 2.0
/
class MysqliDb
{
/
* Static instance of self
*
* #var MysqliDb
*/
protected static $_instance;
/**
* Table prefix
*
* #var string
*/
protected static $_prefix;
/**
* MySQLi instance
*
* #var mysqli
*/
protected $_mysqli;
/**
* The SQL query to be prepared and executed
*
* #var string
*/
protected $_query;
/**
* The previously executed SQL query
*
* #var string
*/
protected $_lastQuery;
/**
* An array that holds where joins
*
* #var array
*/
protected $_join = array();
/**
* An array that holds where conditions 'fieldname' => 'value'
*
* #var array
*/
protected $_where = array();
/**
* Dynamic type list for order by condition value
*/
protected $_orderBy = array();
/**
* Dynamic type list for group by condition value
*/
protected $_groupBy = array();
/**
* Dynamic array that holds a combination of where condition/table data value types and parameter referances
*
* #var array
*/
protected $_bindParams = array(''); // Create the empty 0 index
/**
* Variable which holds an amount of returned rows during get/getOne/select queries
*
* #var string
*/
public $count = 0;
/**
* Variable which holds last statement error
*
* #var string
*/
protected $_stmtError;
/**
* Database credentials
*
* #var string
*/
protected $host;
protected $username;
protected $password;
protected $db;
protected $port;
/**
* Is Subquery object
*
*/
protected $isSubQuery = false;
/**
* #param string $host
* #param string $username
* #param string $password
* #param string $db
* #param int $port
*/
public function __construct($host = NULL, $username = NULL, $password = NULL, $db = NULL, $port = NULL)
{
$this->host = $host;
$this->username = $username;
$this->password = $password;
$this->db = $db;
if($port == NULL)
$this->port = ini_get ('mysqli.default_port');
else
$this->port = $port;
if ($host == null && $username == null && $db == null) {
$this->isSubQuery = true;
return;
}
// for subqueries we do not need database connection and redefine root instance
$this->connect();
$this->setPrefix();
self::$_instance = $this;
}
/**
* A method to connect to the database
*
*/
public function connect()
{
if ($this->isSubQuery)
return;
$this->_mysqli = new mysqli ($this->host, $this->username, $this->password, $this->db, $this->port)
or die('There was a problem connecting to the database');
$this->_mysqli->set_charset ('utf8');
}
/**
* A method of returning the static instance to allow access to the
* instantiated object from within another class.
* Inheriting this class would require reloading connection info.
*
* #uses $db = MySqliDb::getInstance();
*
* #return object Returns the current instance.
*/
public static function getInstance()
{
return self::$_instance;
}
/**
* Reset states after an execution
*
* #return object Returns the current instance.
*/
protected function reset()
{
$this->_where = array();
$this->_join = array();
$this->_orderBy = array();
$this->_groupBy = array();
$this->_bindParams = array(''); // Create the empty 0 index
$this->_query = null;
$this->count = 0;
}
/**
* Method to set a prefix
*
* #param string $prefix Contains a tableprefix
*/
public function setPrefix($prefix = '')
{
self::$_prefix = $prefix;
return $this;
}
/**
* Pass in a raw query and an array containing the parameters to bind to the prepaird statement.
*
* #param string $query Contains a user-provided query.
* #param array $bindParams All variables to bind to the SQL statment.
* #param bool $sanitize If query should be filtered before execution
*
* #return array Contains the returned rows from the query.
*/
public function rawQuery ($query, $bindParams = null, $sanitize = true)
{
$this->_query = $query;
if ($sanitize)
$this->_query = filter_var ($query, FILTER_SANITIZE_STRING,
FILTER_FLAG_NO_ENCODE_QUOTES);
$stmt = $this->_prepareQuery();
if (is_array($bindParams) === true) {
$params = array(''); // Create the empty 0 index
foreach ($bindParams as $prop => $val) {
$params[0] .= $this->_determineType($val);
array_push($params, $bindParams[$prop]);
}
call_user_func_array(array($stmt, 'bind_param'), $this->refValues($params));
}
$stmt->execute();
$this->_stmtError = $stmt->error;
$this->reset();
return $this->_dynamicBindResults($stmt);
}
/**
*
* #param string $query Contains a user-provided select query.
* #param int $numRows The number of rows total to return.
*
* #return array Contains the returned rows from the query.
*/
public function query($query, $numRows = null)
{
$this->_query = filter_var($query, FILTER_SANITIZE_STRING);
$stmt = $this->_buildQuery($numRows);
$stmt->execute();
$this->_stmtError = $stmt->error;
$this->reset();
return $this->_dynamicBindResults($stmt);
}
/**
* A convenient SELECT * function.
*
* #param string $tableName The name of the database table to work with.
* #param integer $numRows The number of rows total to return.
*
* #return array Contains the returned rows from the select query.
*/
public function get($tableName, $numRows = null, $columns = '*')
{
if (empty ($columns))
$columns = '*';
$column = is_array($columns) ? implode(', ', $columns) : $columns;
$this->_query = "SELECT $column FROM " .self::$_prefix . $tableName;
$stmt = $this->_buildQuery($numRows);
if ($this->isSubQuery)
return $this;
$stmt->execute();
$this->_stmtError = $stmt->error;
$this->reset();
return $this->_dynamicBindResults($stmt);
}
/**
* A convenient SELECT * function to get one record.
*
* #param string $tableName The name of the database table to work with.
*
* #return array Contains the returned rows from the select query.
*/
public function getOne($tableName, $columns = '*')
{
$res = $this->get ($tableName, 1, $columns);
if (is_object($res))
return $res;
if (isset($res[0]))
return $res[0];
return null;
}
/**
*
* #param <string $tableName The name of the table.
* #param array $insertData Data containing information for inserting into the DB.
*
* #return boolean Boolean indicating whether the insert query was completed succesfully.
*/
public function insert($tableName, $insertData)
{
if ($this->isSubQuery)
return;
$this->_query = "INSERT into " .self::$_prefix . $tableName;
$stmt = $this->_buildQuery(null, $insertData);
$stmt->execute();
$this->_stmtError = $stmt->error;
$this->reset();
return ($stmt->affected_rows > 0 ? $stmt->insert_id : false);
}
/**
* Update query. Be sure to first call the "where" method.
*
* #param string $tableName The name of the database table to work with.
* #param array $tableData Array of data to update the desired row.
*
* #return boolean
*/
public function update($tableName, $tableData)
{
if ($this->isSubQuery)
return;
$this->_query = "UPDATE " . self::$_prefix . $tableName ." SET ";
$stmt = $this->_buildQuery (null, $tableData);
$status = $stmt->execute();
$this->reset();
$this->_stmtError = $stmt->error;
$this->count = $stmt->affected_rows;
return $status;
}
/**
* Delete query. Call the "where" method first.
*
* #param string $tableName The name of the database table to work with.
* #param integer $numRows The number of rows to delete.
*
* #return boolean Indicates success. 0 or 1.
*/
public function delete($tableName, $numRows = null)
{
if ($this->isSubQuery)
return;
$this->_query = "DELETE FROM " . self::$_prefix . $tableName;
$stmt = $this->_buildQuery($numRows);
$stmt->execute();
$this->_stmtError = $stmt->error;
$this->reset();
return ($stmt->affected_rows > 0);
}
/**
* This method allows you to specify multiple (method chaining optional) AND WHERE statements for SQL queries.
*
* #uses $MySqliDb->where('id', 7)->where('title', 'MyTitle');
*
* #param string $whereProp The name of the database field.
* #param mixed $whereValue The value of the database field.
*
* #return MysqliDb
*/
public function where($whereProp, $whereValue = null, $operator = null)
{
if ($operator)
$whereValue = Array ($operator => $whereValue);
$this->_where[] = Array ("AND", $whereValue, $whereProp);
return $this;
}
/**
* This method allows you to specify multiple (method chaining optional) OR WHERE statements for SQL queries.
*
* #uses $MySqliDb->orWhere('id', 7)->orWhere('title', 'MyTitle');
*
* #param string $whereProp The name of the database field.
* #param mixed $whereValue The value of the database field.
*
* #return MysqliDb
*/
public function orWhere($whereProp, $whereValue = null, $operator = null)
{
if ($operator)
$whereValue = Array ($operator => $whereValue);
$this->_where[] = Array ("OR", $whereValue, $whereProp);
return $this;
}
/**
* This method allows you to concatenate joins for the final SQL statement.
*
* #uses $MySqliDb->join('table1', 'field1 <> field2', 'LEFT')
*
* #param string $joinTable The name of the table.
* #param string $joinCondition the condition.
* #param string $joinType 'LEFT', 'INNER' etc.
*
* #return MysqliDb
*/
public function join($joinTable, $joinCondition, $joinType = '')
{
$allowedTypes = array('LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER');
$joinType = strtoupper (trim ($joinType));
$joinTable = filter_var($joinTable, FILTER_SANITIZE_STRING);
if ($joinType && !in_array ($joinType, $allowedTypes))
die ('Wrong JOIN type: '.$joinType);
$this->_join[$joinType . " JOIN " . self::$_prefix . $joinTable] = $joinCondition;
return $this;
}
/**
* This method allows you to specify multiple (method chaining optional) ORDER BY statements for SQL queries.
*
* #uses $MySqliDb->orderBy('id', 'desc')->orderBy('name', 'desc');
*
* #param string $orderByField The name of the database field.
* #param string $orderByDirection Order direction.
*
* #return MysqliDb
*/
public function orderBy($orderByField, $orderbyDirection = "DESC")
{
$allowedDirection = Array ("ASC", "DESC");
$orderbyDirection = strtoupper (trim ($orderbyDirection));
$orderByField = preg_replace ("/[^-a-z0-9\.\(\),_]+/i",'', $orderByField);
if (empty($orderbyDirection) || !in_array ($orderbyDirection, $allowedDirection))
die ('Wrong order direction: '.$orderbyDirection);
$this->_orderBy[$orderByField] = $orderbyDirection;
return $this;
}
/**
* This method allows you to specify multiple (method chaining optional) GROUP BY statements for SQL queries.
*
* #uses $MySqliDb->groupBy('name');
*
* #param string $groupByField The name of the database field.
*
* #return MysqliDb
*/
public function groupBy($groupByField)
{
$groupByField = preg_replace ("/[^-a-z0-9\.\(\),_]+/i",'', $groupByField);
$this->_groupBy[] = $groupByField;
return $this;
}
/**
* This methods returns the ID of the last inserted item
*
* #return integer The last inserted item ID.
*/
public function getInsertId()
{
return $this->_mysqli->insert_id;
}
/**
* Escape harmful characters which might affect a query.
*
* #param string $str The string to escape.
*
* #return string The escaped string.
*/
public function escape($str)
{
return $this->_mysqli->real_escape_string($str);
}
/**
* Method to call mysqli->ping() to keep unused connections open on
* long-running scripts, or to reconnect timed out connections (if php.ini has
* global mysqli.reconnect set to true). Can't do this directly using object
* since _mysqli is protected.
*
* #return bool True if connection is up
*/
public function ping() {
return $this->_mysqli->ping();
}
/**
* This method is needed for prepared statements. They require
* the data type of the field to be bound with "i" s", etc.
* This function takes the input, determines what type it is,
* and then updates the param_type.
*
* #param mixed $item Input to determine the type.
*
* #return string The joined parameter types.
*/
protected function _determineType($item)
{
switch (gettype($item)) {
case 'NULL':
case 'string':
return 's';
break;
case 'boolean':
case 'integer':
return 'i';
break;
case 'blob':
return 'b';
break;
case 'double':
return 'd';
break;
}
return '';
}
/**
* Helper function to add variables into bind parameters array
*
* #param string Variable value
*/
protected function _bindParam($value) {
$this->_bindParams[0] .= $this->_determineType ($value);
array_push ($this->_bindParams, $value);
}
/**
* Helper function to add variables into bind parameters array in bulk
*
* #param Array Variable with values
*/
protected function _bindParams ($values) {
foreach ($values as $value)
$this->_bindParam ($value);
}
/**
* Helper function to add variables into bind parameters array and will return
* its SQL part of the query according to operator in ' $operator ?' or
* ' $operator ($subquery) ' formats
*
* #param Array Variable with values
*/
protected function _buildPair ($operator, $value) {
if (!is_object($value)) {
$this->_bindParam ($value);
return ' ' . $operator. ' ? ';
}
$subQuery = $value->getSubQuery ();
$this->_bindParams ($subQuery['params']);
return " " . $operator . " (" . $subQuery['query'] . ")";
}
/**
* Abstraction method that will compile the WHERE statement,
* any passed update data, and the desired rows.
* It then builds the SQL query.
*
* #param int $numRows The number of rows total to return.
* #param array $tableData Should contain an array of data for updating the database.
*
* #return mysqli_stmt Returns the $stmt object.
*/
protected function _buildQuery($numRows = null, $tableData = null)
{
$this->_buildJoin();
$this->_buildTableData ($tableData);
$this->_buildWhere();
$this->_buildGroupBy();
$this->_buildOrderBy();
$this->_buildLimit ($numRows);
$this->_lastQuery = $this->replacePlaceHolders ($this->_query, $this->_bindParams);
if ($this->isSubQuery)
return;
// Prepare query
$stmt = $this->_prepareQuery();
// Bind parameters to statement if any
if (count ($this->_bindParams) > 1)
call_user_func_array(array($stmt, 'bind_param'), $this->refValues($this->_bindParams));
return $stmt;
}
/**
* This helper method takes care of prepared statements' "bind_result method
* , when the number of variables to pass is unknown.
*
* #param mysqli_stmt $stmt Equal to the prepared statement object.
*
* #return array The results of the SQL fetch.
*/
protected function _dynamicBindResults(mysqli_stmt $stmt)
{
$parameters = array();
$results = array();
$meta = $stmt->result_metadata();
// if $meta is false yet sqlstate is true, there's no sql error but the query is
// most likely an update/insert/delete which doesn't produce any results
if(!$meta && $stmt->sqlstate) {
return array();
}
$row = array();
while ($field = $meta->fetch_field()) {
$row[$field->name] = null;
$parameters[] = & $row[$field->name];
}
// avoid out of memory bug in php 5.2 and 5.3
// https://github.com/joshcam/PHP-MySQLi-Database-Class/pull/119
if (version_compare (phpversion(), '5.4', '<'))
$stmt->store_result();
call_user_func_array(array($stmt, 'bind_result'), $parameters);
while ($stmt->fetch()) {
$x = array();
foreach ($row as $key => $val) {
$x[$key] = $val;
}
$this->count++;
array_push($results, $x);
}
return $results;
}
/**
* Abstraction method that will build an JOIN part of the query
*/
protected function _buildJoin () {
if (empty ($this->_join))
return;
foreach ($this->_join as $prop => $value)
$this->_query .= " " . $prop . " on " . $value;
}
/**
* Abstraction method that will build an INSERT or UPDATE part of the query
*/
protected function _buildTableData ($tableData) {
if (!is_array ($tableData))
return;
$isInsert = strpos ($this->_query, 'INSERT');
$isUpdate = strpos ($this->_query, 'UPDATE');
if ($isInsert !== false) {
$this->_query .= '(`' . implode(array_keys($tableData), '`, `') . '`)';
$this->_query .= ' VALUES(';
}
foreach ($tableData as $column => $value) {
if ($isUpdate !== false)
$this->_query .= "`" . $column . "` = ";
// Subquery value
if (is_object ($value)) {
$this->_query .= $this->_buildPair ("", $value) . ", ";
continue;
}
// Simple value
if (!is_array ($value)) {
$this->_bindParam ($value);
$this->_query .= '?, ';
continue;
}
// Function value
$key = key ($value);
$val = $value[$key];
switch ($key) {
case '[I]':
$this->_query .= $column . $val . ", ";
break;
case '[F]':
$this->_query .= $val[0] . ", ";
if (!empty ($val[1]))
$this->_bindParams ($val[1]);
break;
case '[N]':
if ($val == null)
$this->_query .= "!" . $column . ", ";
else
$this->_query .= "!" . $val . ", ";
break;
default:
die ("Wrong operation");
}
}
$this->_query = rtrim($this->_query, ', ');
if ($isInsert !== false)
$this->_query .= ')';
}
/**
* Abstraction method that will build the part of the WHERE conditions
*/
protected function _buildWhere () {
if (empty ($this->_where))
return;
//Prepair the where portion of the query
$this->_query .= ' WHERE ';
// Remove first AND/OR concatenator
$this->_where[0][0] = '';
foreach ($this->_where as $cond) {
list ($concat, $wValue, $wKey) = $cond;
$this->_query .= " " . $concat ." " . $wKey;
// Empty value (raw where condition in wKey)
if ($wValue === null)
continue;
// Simple = comparison
if (!is_array ($wValue))
$wValue = Array ('=' => $wValue);
$key = key ($wValue);
$val = $wValue[$key];
switch (strtolower ($key)) {
case '0':
$this->_bindParams ($wValue);
break;
case 'not in':
case 'in':
$comparison = ' ' . $key . ' (';
if (is_object ($val)) {
$comparison .= $this->_buildPair ("", $val);
} else {
foreach ($val as $v) {
$comparison .= ' ?,';
$this->_bindParam ($v);
}
}
$this->_query .= rtrim($comparison, ',').' ) ';
break;
case 'not between':
case 'between':
$this->_query .= " $key ? AND ? ";
$this->_bindParams ($val);
break;
default:
$this->_query .= $this->_buildPair ($key, $val);
}
}
}
/**
* Abstraction method that will build the GROUP BY part of the WHERE statement
*
*/
protected function _buildGroupBy () {
if (empty ($this->_groupBy))
return;
$this->_query .= " GROUP BY ";
foreach ($this->_groupBy as $key => $value)
$this->_query .= $value . ", ";
$this->_query = rtrim($this->_query, ', ') . " ";
}
/**
* Abstraction method that will build the LIMIT part of the WHERE statement
*
* #param int $numRows The number of rows total to return.
*/
protected function _buildOrderBy () {
if (empty ($this->_orderBy))
return;
$this->_query .= " ORDER BY ";
foreach ($this->_orderBy as $prop => $value)
$this->_query .= $prop . " " . $value . ", ";
$this->_query = rtrim ($this->_query, ', ') . " ";
}
/**
* Abstraction method that will build the LIMIT part of the WHERE statement
*
* #param int $numRows The number of rows total to return.
*/
protected function _buildLimit ($numRows) {
if (!isset ($numRows))
return;
if (is_array ($numRows))
$this->_query .= ' LIMIT ' . (int)$numRows[0] . ', ' . (int)$numRows[1];
else
$this->_query .= ' LIMIT ' . (int)$numRows;
}
/**
* Method attempts to prepare the SQL query
* and throws an error if there was a problem.
*
* #return mysqli_stmt
*/
protected function _prepareQuery()
{
if (!$stmt = $this->_mysqli->prepare($this->_query)) {
trigger_error("Problem preparing query ($this->_query) " . $this->_mysqli->error, E_USER_ERROR);
}
return $stmt;
}
/**
* Close connection
*/
public function __destruct()
{
if (!$this->isSubQuery)
return;
if ($this->_mysqli)
$this->_mysqli->close();
}
/**
* #param array $arr
*
* #return array
*/
protected function refValues($arr)
{
//Reference is required for PHP 5.3+
if (strnatcmp(phpversion(), '5.3') >= 0) {
$refs = array();
foreach ($arr as $key => $value) {
$refs[$key] = & $arr[$key];
}
return $refs;
}
return $arr;
}
/**
* Function to replace ? with variables from bind variable
* #param string $str
* #param Array $vals
*
* #return string
*/
protected function replacePlaceHolders ($str, $vals) {
$i = 1;
$newStr = "";
while ($pos = strpos ($str, "?")) {
$val = $vals[$i++];
if (is_object ($val))
$val = '[object]';
$newStr .= substr ($str, 0, $pos) . $val;
$str = substr ($str, $pos + 1);
}
return $newStr;
}
/**
* Method returns last executed query
*
* #return string
*/
public function getLastQuery () {
return $this->_lastQuery;
}
/**
* Method returns mysql error
*
* #return string
*/
public function getLastError () {
return $this->_stmtError . " " . $this->_mysqli->error;
}
/**
* Mostly internal method to get query and its params out of subquery object
* after get() and getAll()
*
* #return array
*/
public function getSubQuery () {
if (!$this->isSubQuery)
return null;
array_shift ($this->_bindParams);
$val = Array ('query' => $this->_query,
'params' => $this->_bindParams
);
$this->reset();
return $val;
}
/* Helper functions */
/**
* Method returns generated interval function as a string
*
* #param string interval in the formats:
* "1", "-1d" or "- 1 day" -- For interval - 1 day
* Supported intervals [s]econd, [m]inute, [h]hour, [d]day, [M]onth, [Y]ear
* Default null;
* #param string Initial date
*
* #return string
*/
public function interval ($diff, $func = "NOW()") {
$types = Array ("s" => "second", "m" => "minute", "h" => "hour", "d" => "day", "M" => "month", "Y" => "year");
$incr = '+';
$items = '';
$type = 'd';
if ($diff && preg_match('/([+-]?) ?([0-9]+) ?([a-zA-Z]?)/',$diff, $matches)) {
if (!empty ($matches[1])) $incr = $matches[1];
if (!empty ($matches[2])) $items = $matches[2];
if (!empty ($matches[3])) $type = $matches[3];
if (!in_array($type, array_keys($types)))
trigger_error ("invalid interval type in '{$diff}'");
$func .= " ".$incr ." interval ". $items ." ".$types[$type] . " ";
}
return $func;
}
/**
* Method returns generated interval function as an insert/update function
*
* #param string interval in the formats:
* "1", "-1d" or "- 1 day" -- For interval - 1 day
* Supported intervals [s]econd, [m]inute, [h]hour, [d]day,
[M]onth, [Y]ear
* Default null;
* #param string Initial date
*
* #return array
*/
public function now ($diff = null, $func = "NOW()") {
return Array ("[F]" => Array($this->interval($diff, $func)));
}
/**
* Method generates incremental function call
* #param int increment amount. 1 by default
*/
public function inc($num = 1) {
return Array ("[I]" => "+" . (int)$num);
}
/**
* Method generates decrimental function call
* #param int increment amount. 1 by default
*/
public function dec ($num = 1) {
return Array ("[I]" => "-" . (int)$num);
}
/**
* Method generates change boolean function call
* #param string column name. null by default
*/
public function not ($col = null) {
return Array ("[N]" => (string)$col);
}
/**
* Method generates user defined function call
* #param string user function body
*/
public function func ($expr, $bindParams = null) {
return Array ("[F]" => Array($expr, $bindParams));
}
/**
* Method creates new mysqlidb object for a subquery generation
*/
public static function subQuery()
{
return new MysqliDb();
}
/**
* Method returns a copy of a mysqlidb subquery object
*
* #param object new mysqlidb object
*/
public function copy ()
{
return clone $this;
}
/**
* Begin a transaction
*
* #uses mysqli->autocommit(false)
* #uses register_shutdown_function(array($this, "_transaction_shutdown_check"))
*/
public function startTransaction () {
$this->_mysqli->autocommit (false);
$this->_transaction_in_progress = true;
register_shutdown_function (array ($this, "_transaction_status_check"));
}
/**
* Transaction commit
*
* #uses mysqli->commit();
* #uses mysqli->autocommit(true);
*/
public function commit () {
$this->_mysqli->commit ();
$this->_transaction_in_progress = false;
$this->_mysqli->autocommit (true);
}
/**
* Transaction rollback function
*
* #uses mysqli->rollback();
* #uses mysqli->autocommit(true);
*/
public function rollback () {
$this->_mysqli->rollback ();
$this->_transaction_in_progress = false;
$this->_mysqli->autocommit (true);
}
/**
* Shutdown handler to rollback uncommited operations in order to keep
* atomic operations sane.
*
* #uses mysqli->rollback();
*/
public function _transaction_status_check () {
if (!$this->_transaction_in_progress)
return;
$this->rollback ();
}
} // END class
I have downloaded the SafeMySQL class which I will post below. I would like to extend this database class to all of my other classes throughout the site that call queries. Currently, I have the main db connector set as a global variable, but I have to call it inside each class constructor and all of the class's methods. Surely there has to be an easier way?
Here is the DB class:
class SafeMySQL
{
private $conn;
private $stats;
private $emode;
private $exname;
private $defaults = array(
'host' => 'localhost',
'user' => '',
'pass' => '',
'db' => '',
'port' => NULL,
'socket' => NULL,
'pconnect' => FALSE,
'charset' => 'utf8',
'errmode' => 'error', //or exception
'exception' => 'Exception', //Exception class name
);
const RESULT_ASSOC = MYSQLI_ASSOC;
const RESULT_NUM = MYSQLI_NUM;
public function __construct($opt = array())
{
$opt = array_merge($this->defaults,$opt);
$this->emode = $opt['errmode'];
$this->exname = $opt['exception'];
if ($opt['pconnect'])
{
$opt['host'] = "p:".$opt['host'];
}
#$this->conn = mysqli_connect($opt['host'], $opt['user'], $opt['pass'], $opt['db'], $opt['port'], $opt['socket']);
if ( !$this->conn )
{
$this->error(mysqli_connect_errno()." ".mysqli_connect_error());
}
mysqli_set_charset($this->conn, $opt['charset']) or $this->error(mysqli_error($this->conn));
unset($opt); // I am paranoid
}
/**
* Conventional function to run a query with placeholders. A mysqli_query wrapper with placeholders support
*
* Examples:
* $db->query("DELETE FROM table WHERE id=?i", $id);
*
* #param string $query - an SQL query with placeholders
* #param mixed $arg,... unlimited number of arguments to match placeholders in the query
* #return resource|FALSE whatever mysqli_query returns
*/
public function query()
{
return $this->rawQuery($this->prepareQuery(func_get_args()));
}
/**
* Conventional function to fetch single row.
*
* #param resource $result - myqli result
* #param int $mode - optional fetch mode, RESULT_ASSOC|RESULT_NUM, default RESULT_ASSOC
* #return array|FALSE whatever mysqli_fetch_array returns
*/
public function fetch($result,$mode=self::RESULT_ASSOC)
{
return mysqli_fetch_array($result, $mode);
}
/**
* Conventional function to get number of affected rows.
*
* #return int whatever mysqli_affected_rows returns
*/
public function affectedRows()
{
return mysqli_affected_rows ($this->conn);
}
/**
* Conventional function to get last insert id.
*
* #return int whatever mysqli_insert_id returns
*/
public function insertId()
{
return mysqli_insert_id($this->conn);
}
/**
* Conventional function to get number of rows in the resultset.
*
* #param resource $result - myqli result
* #return int whatever mysqli_num_rows returns
*/
public function numRows($result)
{
return mysqli_num_rows($result);
}
/**
* Conventional function to free the resultset.
*/
public function free($result)
{
mysqli_free_result($result);
}
/**
* Helper function to get scalar value right out of query and optional arguments
*
* Examples:
* $name = $db->getOne("SELECT name FROM table WHERE id=1");
* $name = $db->getOne("SELECT name FROM table WHERE id=?i", $id);
*
* #param string $query - an SQL query with placeholders
* #param mixed $arg,... unlimited number of arguments to match placeholders in the query
* #return string|FALSE either first column of the first row of resultset or FALSE if none found
*/
public function getOne()
{
$query = $this->prepareQuery(func_get_args());
if ($res = $this->rawQuery($query))
{
$row = $this->fetch($res);
if (is_array($row)) {
return reset($row);
}
$this->free($res);
}
return FALSE;
}
/**
* Helper function to get single row right out of query and optional arguments
*
* Examples:
* $data = $db->getRow("SELECT * FROM table WHERE id=1");
* $data = $db->getOne("SELECT * FROM table WHERE id=?i", $id);
*
* #param string $query - an SQL query with placeholders
* #param mixed $arg,... unlimited number of arguments to match placeholders in the query
* #return array|FALSE either associative array contains first row of resultset or FALSE if none found
*/
public function getRow()
{
$query = $this->prepareQuery(func_get_args());
if ($res = $this->rawQuery($query)) {
$ret = $this->fetch($res);
$this->free($res);
return $ret;
}
return FALSE;
}
/**
* Helper function to get single column right out of query and optional arguments
*
* Examples:
* $ids = $db->getCol("SELECT id FROM table WHERE cat=1");
* $ids = $db->getCol("SELECT id FROM tags WHERE tagname = ?s", $tag);
*
* #param string $query - an SQL query with placeholders
* #param mixed $arg,... unlimited number of arguments to match placeholders in the query
* #return array|FALSE either enumerated array of first fields of all rows of resultset or FALSE if none found
*/
public function getCol()
{
$ret = array();
$query = $this->prepareQuery(func_get_args());
if ( $res = $this->rawQuery($query) )
{
while($row = $this->fetch($res))
{
$ret[] = reset($row);
}
$this->free($res);
}
return $ret;
}
/**
* Helper function to get all the rows of resultset right out of query and optional arguments
*
* Examples:
* $data = $db->getAll("SELECT * FROM table");
* $data = $db->getAll("SELECT * FROM table LIMIT ?i,?i", $start, $rows);
*
* #param string $query - an SQL query with placeholders
* #param mixed $arg,... unlimited number of arguments to match placeholders in the query
* #return array enumerated 2d array contains the resultset. Empty if no rows found.
*/
public function getAll()
{
$ret = array();
$query = $this->prepareQuery(func_get_args());
if ( $res = $this->rawQuery($query) )
{
while($row = $this->fetch($res))
{
$ret[] = $row;
}
$this->free($res);
}
return $ret;
}
/**
* Helper function to get all the rows of resultset into indexed array right out of query and optional arguments
*
* Examples:
* $data = $db->getInd("id", "SELECT * FROM table");
* $data = $db->getInd("id", "SELECT * FROM table LIMIT ?i,?i", $start, $rows);
*
* #param string $index - name of the field which value is used to index resulting array
* #param string $query - an SQL query with placeholders
* #param mixed $arg,... unlimited number of arguments to match placeholders in the query
* #return array - associative 2d array contains the resultset. Empty if no rows found.
*/
public function getInd()
{
$args = func_get_args();
$index = array_shift($args);
$query = $this->prepareQuery($args);
$ret = array();
if ( $res = $this->rawQuery($query) )
{
while($row = $this->fetch($res))
{
$ret[$row[$index]] = $row;
}
$this->free($res);
}
return $ret;
}
/**
* Helper function to get a dictionary-style array right out of query and optional arguments
*
* Examples:
* $data = $db->getIndCol("name", "SELECT name, id FROM cities");
*
* #param string $index - name of the field which value is used to index resulting array
* #param string $query - an SQL query with placeholders
* #param mixed $arg,... unlimited number of arguments to match placeholders in the query
* #return array - associative array contains key=value pairs out of resultset. Empty if no rows found.
*/
public function getIndCol()
{
$args = func_get_args();
$index = array_shift($args);
$query = $this->prepareQuery($args);
$ret = array();
if ( $res = $this->rawQuery($query) )
{
while($row = $this->fetch($res))
{
$key = $row[$index];
unset($row[$index]);
$ret[$key] = reset($row);
}
$this->free($res);
}
return $ret;
}
/**
* Function to parse placeholders either in the full query or a query part
* unlike native prepared statements, allows ANY query part to be parsed
*
* useful for debug
* and EXTREMELY useful for conditional query building
* like adding various query parts using loops, conditions, etc.
* already parsed parts have to be added via ?p placeholder
*
* Examples:
* $query = $db->parse("SELECT * FROM table WHERE foo=?s AND bar=?s", $foo, $bar);
* echo $query;
*
* if ($foo) {
* $qpart = $db->parse(" AND foo=?s", $foo);
* }
* $data = $db->getAll("SELECT * FROM table WHERE bar=?s ?p", $bar, $qpart);
*
* #param string $query - whatever expression contains placeholders
* #param mixed $arg,... unlimited number of arguments to match placeholders in the expression
* #return string - initial expression with placeholders substituted with data.
*/
public function parse()
{
return $this->prepareQuery(func_get_args());
}
/**
* function to implement whitelisting feature
* sometimes we can't allow a non-validated user-supplied data to the query even through placeholder
* especially if it comes down to SQL OPERATORS
*
* Example:
*
* $order = $db->whiteList($_GET['order'], array('name','price'));
* $dir = $db->whiteList($_GET['dir'], array('ASC','DESC'));
* if (!$order || !dir) {
* throw new http404(); //non-expected values should cause 404 or similar response
* }
* $sql = "SELECT * FROM table ORDER BY ?p ?p LIMIT ?i,?i"
* $data = $db->getArr($sql, $order, $dir, $start, $per_page);
*
* #param string $iinput - field name to test
* #param array $allowed - an array with allowed variants
* #param string $default - optional variable to set if no match found. Default to false.
* #return string|FALSE - either sanitized value or FALSE
*/
public function whiteList($input,$allowed,$default=FALSE)
{
$found = array_search($input,$allowed);
return ($found === FALSE) ? $default : $allowed[$found];
}
/**
* function to filter out arrays, for the whitelisting purposes
* useful to pass entire superglobal to the INSERT or UPDATE query
* OUGHT to be used for this purpose,
* as there could be fields to which user should have no access to.
*
* Example:
* $allowed = array('title','url','body','rating','term','type');
* $data = $db->filterArray($_POST,$allowed);
* $sql = "INSERT INTO ?n SET ?u";
* $db->query($sql,$table,$data);
*
* #param array $input - source array
* #param array $allowed - an array with allowed field names
* #return array filtered out source array
*/
public function filterArray($input,$allowed)
{
foreach(array_keys($input) as $key )
{
if ( !in_array($key,$allowed) )
{
unset($input[$key]);
}
}
return $input;
}
/**
* Function to get last executed query.
*
* #return string|NULL either last executed query or NULL if were none
*/
public function lastQuery()
{
$last = end($this->stats);
return $last['query'];
}
/**
* Function to get all query statistics.
*
* #return array contains all executed queries with timings and errors
*/
public function getStats()
{
return $this->stats;
}
/**
* private function which actually runs a query against Mysql server.
* also logs some stats like profiling info and error message
*
* #param string $query - a regular SQL query
* #return mysqli result resource or FALSE on error
*/
private function rawQuery($query)
{
$start = microtime(TRUE);
$res = mysqli_query($this->conn, $query);
$timer = microtime(TRUE) - $start;
$this->stats[] = array(
'query' => $query,
'start' => $start,
'timer' => $timer,
);
if (!$res)
{
$error = mysqli_error($this->conn);
end($this->stats);
$key = key($this->stats);
$this->stats[$key]['error'] = $error;
$this->cutStats();
$this->error("$error. Full query: [$query]");
}
$this->cutStats();
return $res;
}
private function prepareQuery($args)
{
$query = '';
$raw = array_shift($args);
$array = preg_split('~(\?[nsiuap])~u',$raw,null,PREG_SPLIT_DELIM_CAPTURE);
$anum = count($args);
$pnum = floor(count($array) / 2);
if ( $pnum != $anum )
{
$this->error("Number of args ($anum) doesn't match number of placeholders ($pnum) in [$raw]");
}
foreach ($array as $i => $part)
{
if ( ($i % 2) == 0 )
{
$query .= $part;
continue;
}
$value = array_shift($args);
switch ($part)
{
case '?n':
$part = $this->escapeIdent($value);
break;
case '?s':
$part = $this->escapeString($value);
break;
case '?i':
$part = $this->escapeInt($value);
break;
case '?a':
$part = $this->createIN($value);
break;
case '?u':
$part = $this->createSET($value);
break;
case '?p':
$part = $value;
break;
}
$query .= $part;
}
return $query;
}
private function escapeInt($value)
{
if ($value === NULL)
{
return 'NULL';
}
if(!is_numeric($value))
{
$this->error("Integer (?i) placeholder expects numeric value, ".gettype($value)." given");
return FALSE;
}
if (is_float($value))
{
$value = number_format($value, 0, '.', ''); // may lose precision on big numbers
}
return $value;
}
private function escapeString($value)
{
if ($value === NULL)
{
return 'NULL';
}
return "'".mysqli_real_escape_string($this->conn,$value)."'";
}
private function escapeIdent($value)
{
if ($value)
{
return "`".str_replace("`","``",$value)."`";
} else {
$this->error("Empty value for identifier (?n) placeholder");
}
}
private function createIN($data)
{
if (!is_array($data))
{
$this->error("Value for IN (?a) placeholder should be array");
return;
}
if (!$data)
{
return 'NULL';
}
$query = $comma = '';
foreach ($data as $value)
{
$query .= $comma.$this->escapeString($value);
$comma = ",";
}
return $query;
}
private function createSET($data)
{
if (!is_array($data))
{
$this->error("SET (?u) placeholder expects array, ".gettype($data)." given");
return;
}
if (!$data)
{
$this->error("Empty array for SET (?u) placeholder");
return;
}
$query = $comma = '';
foreach ($data as $key => $value)
{
$query .= $comma.$this->escapeIdent($key).'='.$this->escapeString($value);
$comma = ",";
}
return $query;
}
private function error($err)
{
$err = __CLASS__.": ".$err;
if ( $this->emode == 'error' )
{
$err .= ". Error initiated in ".$this->caller().", thrown";
trigger_error($err,E_USER_ERROR);
} else {
throw new $this->exname($err);
}
}
private function caller()
{
$trace = debug_backtrace();
$caller = '';
foreach ($trace as $t)
{
if ( isset($t['class']) && $t['class'] == __CLASS__ )
{
$caller = $t['file']." on line ".$t['line'];
} else {
break;
}
}
return $caller;
}
/**
* On a long run we can eat up too much memory with mere statsistics
* Let's keep it at reasonable size, leaving only last 100 entries.
*/
private function cutStats()
{
if ( count($this->stats) > 100 )
{
reset($this->stats);
$first = key($this->stats);
unset($this->stats[$first]);
}
}
}
//HOW I'M CURRENTLY CONNECTING TO THE DATABASE & CREATING GLOBAL VAR
global $db;
$db = new SafeMySQL('localhost', 'user', 'password', 'database');
This is the class I would like to extend the above database class to:
class News
{
public $news_id;
var $author;
var $title;
var $body;
var $date;
var $comments_count;
function __construct($id)
{
global $db;
$row = $db->getRow('SELECT *
FROM news_articles
WHERE id = ?i', $id);
$this->news_id = $row[id];
$this->author = $row[author];
$this->title = $row[title];
$this->body = $row[body];
$this->date = $row[date];
$this->comments_count = $this->countComments();
}
public static function getAllArticles(){
global $db;
$all_articles_array = $db->getAll('SELECT id
FROM news_articles ORDER BY date DESC');
return $all_articles_array;
}
Please do not use the word 'extends'. It has a very special meaning and may confuse a reader. You rather need to 'use' another class' instance in this .
Although in my opinion using global keyword for the site-wide global variables is all right, you'd be teared in pieces if spotted by local 'global police'. So, it's safer to pass a $db object into constructor and assign it as a class property:
class News
{
public $news_id;
private $db;
var $author;
var $title;
var $body;
var $date;
var $comments_count;
function __construct($db, $id)
{
$this->db = $db;
$sql = 'SELECT * FROM news_articles WHERE id = ?i';
$row = $this->db->getRow($sql, $id);
$this->news_id = $row['id'];
$this->author = $row['author'];
$this->title = $row['title'];
$this->body = $row['body'];
$this->date = $row['date'];
$this->comments_count = $this->countComments();
}
public static function getAllArticlesIds()
{
$sql = 'SELECT id FROM news_articles ORDER BY date DESC';
return $thus->db->getCol($sql);
}
}
Note that I renamed the other method and used getCol() method here as you are selecting only one column.
However i don't quite understand why do you set some properties in the constructor. It seems you are confusing two classes - News and Article. It's for the single Article object you have to initialize it's properties in the constructor. While for the News I doubt it is the right way.
I'm not sure if I understand your problem. However, I notice that SafeMySql does not provide the connection handle. It might help to add this:
/**
* Function to get the connection handle.
* Addition to original SafeDB.
*
* Examples:
* mysqli_autocommit($db->getHandle(),FALSE);
* mysqli_commit($db->getHandle());
*
* #param string $getHandle - an SQL connection handle
* #return object
*/
public function getHandle()
{
return $this->conn;
}
I want to include those pages from my Magento website's sitemap.xml which should not be indexed by search engines. For this I have copied Catalog/Category.php Cms/Page.php to my corresponding app/code/local directories and I'm modifying it.
I have modified the Page.php successfully to exclude specific CMS pages from my sitemap.xml. I followed the directions given here
As I'm not familiar with PHP, I don't know what changes to make to adapt this solution for the Category.php file.
Here's my present Category.php file which is not excluding any specific category pages I want from sitemap.xml:
<?php
class Mage_Sitemap_Model_Resource_Catalog_Category extends Mage_Core_Model_Resource_Db_Abstract
{
/**
* Collection Zend Db select
*
* #var Zend_Db_Select
*/
protected $_select;
/**
* Attribute cache
*
* #var array
*/
protected $_attributesCache = array();
/**
* Init resource model (catalog/category)
*
*/
protected function _construct()
{
$this->_init('catalog/category', 'entity_id');
}
/**
* Get category collection array
*
* #param unknown_type $storeId
* #return array
*/
public function getCollection($storeId)
{
$categories = array();
$store = Mage::app()->getStore($storeId);
/* #var $store Mage_Core_Model_Store */
if (!$store) {
return false;
}
$this->_select = $this->_getWriteAdapter()->select()
->from($this->getMainTable())
->where($this->getIdFieldName() . '=?', $store->getRootCategoryId());
$categoryRow = $this->_getWriteAdapter()->fetchRow($this->_select);
if (!$categoryRow) {
return false;
}
$urConditions = array(
'e.entity_id=ur.category_id',
$this->_getWriteAdapter()->quoteInto('ur.store_id=?', $store->getId()),
'ur.product_id IS NULL',
$this->_getWriteAdapter()->quoteInto('ur.is_system=?', 1),
);
$this->_select = $this->_getWriteAdapter()->select()
->from(array('e' => $this->getMainTable()), array($this->getIdFieldName()))
->joinLeft(
array('ur' => $this->getTable('core/url_rewrite')),
join(' AND ', $urConditions),
array('url'=>'request_path')
)
->where('e.path LIKE ?', $categoryRow['path'] . '/%');
$this->_addFilter($storeId, 'is_active', 1);
$query = $this->_getWriteAdapter()->query($this->_select);
while ($row = $query->fetch()) {
$category = $this->_prepareCategory($row);
$categories[$category->getId()] = $category;
}
return $categories;
}
/**
* Prepare category
*
* #param array $categoryRow
* #return Varien_Object
*/
protected function _prepareCategory(array $categoryRow)
{
$category = new Varien_Object();
$category->setId($categoryRow[$this->getIdFieldName()]);
$categoryUrl = !empty($categoryRow['url']) ? $categoryRow['url'] : 'catalog/category/view/id/' . $category->getId();
$category->setUrl($categoryUrl);
return $category;
}
/**
* Add attribute to filter
*
* #param int $storeId
* #param string $attributeCode
* #param mixed $value
* #param string $type
* #return Zend_Db_Select
*/
protected function _addFilter($storeId, $attributeCode, $value, $type = '=')
{
if (!isset($this->_attributesCache[$attributeCode])) {
$attribute = Mage::getSingleton('catalog/category')->getResource()->getAttribute($attributeCode);
$this->_attributesCache[$attributeCode] = array(
'entity_type_id' => $attribute->getEntityTypeId(),
'attribute_id' => $attribute->getId(),
'table' => $attribute->getBackend()->getTable(),
'is_global' => $attribute->getIsGlobal(),
'backend_type' => $attribute->getBackendType()
);
}
$attribute = $this->_attributesCache[$attributeCode];
if (!$this->_select instanceof Zend_Db_Select) {
return false;
}
switch ($type) {
case '=':
$conditionRule = '=?';
break;
case 'in':
$conditionRule = ' IN(?)';
break;
default:
return false;
break;
}
if ($attribute['backend_type'] == 'static') {
$this->_select->where('e.' . $attributeCode . $conditionRule, $value);
} else {
$this->_select->join(
array('t1_'.$attributeCode => $attribute['table']),
'e.entity_id=t1_'.$attributeCode.'.entity_id AND t1_'.$attributeCode.'.store_id=0',
array()
)
->where('t1_'.$attributeCode.'.attribute_id=?', $attribute['attribute_id']);
if ($attribute['is_global']) {
$this->_select->where('t1_'.$attributeCode.'.value'.$conditionRule, $value);
} else {
$ifCase = $this->_select->getAdapter()->getCheckSql('t2_'.$attributeCode.'.value_id > 0', 't2_'.$attributeCode.'.value', 't1_'.$attributeCode.'.value');
$this->_select->joinLeft(
array('t2_'.$attributeCode => $attribute['table']),
$this->_getWriteAdapter()->quoteInto('t1_'.$attributeCode.'.entity_id = t2_'.$attributeCode.'.entity_id AND t1_'.$attributeCode.'.attribute_id = t2_'.$attributeCode.'.attribute_id AND t2_'.$attributeCode.'.store_id=?', $storeId),
array()
)
->where('('.$ifCase.')'.$conditionRule, $value);
}
}
return $this->_select;
}
}
Edit: Many thanks to Luke in his comment below! Here is what worked for me. I had to modify the code a bit since $category isn't defined until after the if statement.
while ($row = $query->fetch()) {
// Added to exclude specific categories
if(in_array($this->_prepareCategory($row)->getId(), $this->ignoreCategories))
{
continue;
}
$category = $this->_prepareCategory($row);
$categories[$category->getId()] = $category;
}
I created the class property for the category IDs just as Luke had said:
protected $ignoreCategories = array("2","3","16");//Replace the numbers with your category IDs
For a quick solution add the code below, A much better way would be to add an attribute to the category EAV then use that for checking if it should be included in the sitemap output.
For the quick hack, add an array as a class property for the category IDs that should be ignored,
protected $ignoreCategories = array("2","3","16");
Then near the bottom of the getCollection() function check for these ID's,
while ($row = $query->fetch()) {
// Added to exclude specific categories
if(in_array($category->getId(), $this->ignoreCategories)
{
continue;
}
$category = $this->_prepareCategory($row);
$categories[$category->getId()] = $category;
}