User::updatemain($set, $where);
This gives Fatal error: Using $this when not in object context
My user class extends from Dbase class and here is user class function:
public static function activate($set, $where) {
return $this->updatemain($set, $where);
here is dbase class (some part of):
private function query($sql = null, $params = null) {
if (!empty($sql)) {
$this->_last_statement = $sql;
if ($this->_db_object == null) {
$this->connect();
}
try {
$statement = $this->_db_object->prepare($sql, $this->_driver_options);
$params = Helper::makeArray($params);
$x = 1;
if (count($params)) {
foreach ($params as $param) {
$statement->bindValue($x, $param);
$x++;
}
}
if (!$statement->execute() || $statement->errorCode() != '0000') {
$error = $statement->errorInfo();
throw new PDOException("Database error {$error[0]} : {$error[2]}, driver error code is {$error[1]}");
exit;
}
//echo $sql;
return $statement;
} catch (PDOException $e) {
echo $this->formatException($e);
exit;
}
}
}
public function updatemain($set, $where) {
return $this->query($sql, $params);
}
this is part of Dbase class
You are calling static method so there is no $this in that context.
If you want to call other static method from given class then use self::method() but if you want to call non-static method you've got problem. First you have to create new object.
When you use static methods, you can't use $this inside
public static function activate($set, $where) {
return self::updatemain($set, $where);
}
Or you have to use singelton design
EDIT
Best solution - rewrite your class to one point access to DB object. And create Model classes to DB access. See my example code below:
core AppCore
<?php
class AppCore
{
public static $config = array();
public static $ormInit = false;
public static function init($config)
{
self::$config = array_merge(self::$config, $config);
}
public static function db($table)
{
// ORM - see http://idiorm.readthedocs.org/en/latest
if (!self::$ormInit) {
ORM::configure(self::$config['db']['connection']);
ORM::configure('username', self::$config['db']['username']);
ORM::configure('password', self::$config['db']['password']);
self::$ormInit = true;
}
return ORM::for_table($table);
}
}
User model
<?php
class UserModel
{
const TABLE = 'user';
public static function findById($u_id)
{
$result = AppCore::db(self::TABLE)->where('u_id', $u_id)->find_one();
return $result ? $result->as_array() : null;
}
}
AppCore init section
AppCore::init(array(
'db' => array(
'connection' => "mysql:dbname={$db};host={$host}",
'username' => $user,
'password' => $pass
),
));
i hope it help make your code better
Related
How would one rewrite the following ...
class crunch {
private $funcs = [];
public function set($name, $function) {
$this->funcs[$name] = $function;
}
public function call($function, $data=false) {
if (isset($this->funcs[$function]) && is_callable($this->funcs[$function])) {
return $this->funcs[$function]($data);
}
}
}
$db = 'dbhandle';
$crunch = new crunch();
$crunch->set('myfunction', function($data) {
global $db;
echo 'db = '. $db .'<br>'. json_encode( $data );
});
$crunch->call('myfunction', [123,'asd']);
... which correctly outputs ...
db = dbhandle
[123,"asd"]
... to remove the ugly global requirement when using frequently used variables/handles within dynamically added functions?
Normally, I'd define the global on construction as follows, but this understandably fails with the fatal error Uncaught Error: Using $this when not in object context ...
class crunch {
private $db;
private $funcs = [];
public function __construct($db) {
$this->db = $db;
}
public function set($name, $function) {
$this->funcs[$name] = $function;
}
public function call($function, $data=false) {
if (isset($this->funcs[$function]) && is_callable($this->funcs[$function])) {
return $this->funcs[$function]($data);
}
}
}
$db = 'dbhandle';
$crunch = new crunch($db);
$crunch->set('myfunction', function($data) {
echo 'db = '. $this->db .'<br>'. json_encode( $data );
});
$crunch->call('myfunction', [123,'asd']);
What's the cleanest way to accomplish the goal?
EDIT: As #Rajdeep points out, I could pass $db within the $crunch->set() function. But I'd like to avoid this, since each dynamic function could reference anywhere from 0-5 of these private variables, and it would be inelegant to have to call all 5 with every $crunch->set().
Instead of creating a private instance variable $db, you could simply pass this variable to the call() method. Your code should be like this:
class crunch {
private $funcs = [];
public function set($name, $function) {
$this->funcs[$name] = $function;
}
public function call($function, $data=false, $db) {
if (isset($this->funcs[$function]) && is_callable($this->funcs[$function])) {
return $this->funcs[$function]($data, $db);
}
}
}
$db = 'dbhandle';
$crunch = new crunch();
$crunch->set('myfunction', function($data, $db){
echo 'db = '. $db .'<br>'. json_encode( $data );
});
$crunch->call('myfunction', [123,'asd'], $db);
Output:
db = dbhandle
[123,"asd"]
Update(1):
In case you want to access $db as instance variable only, the solution would be like this:
class crunch {
public $db;
private $funcs = [];
public function __construct($db) {
$this->db = $db;
}
public function set($name, $function) {
$this->funcs[$name] = $function;
}
public function call($function, $data=false) {
if (isset($this->funcs[$function]) && is_callable($this->funcs[$function])) {
return $this->funcs[$function]($this, $data);
}
}
}
$db = 'dbhandle';
$crunch = new crunch($db);
$crunch->set('myfunction', function($crunch, $data) {
echo 'db = '. $crunch->db .'<br>'. json_encode( $data );
});
$crunch->call('myfunction', [123,'asd']);
Note that you have to make $db as public member variable, otherwise it would be inaccessible while calling the set() method.
I'm trying to create a basic MVC 'framework' but i have some problems with geting data from Models to Views.
class Controller
{
public $_database = null;
public $_model = null;
public function __construct()
{
$this->connect();
}
private function connect()
{
$options = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ, PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING);
$this->_database = new PDO(DB_TYPE . ':host=' . DB_HOST . ';dbname=' . DB_NAME . ';charset=' . DB_CHARSET, DB_USER, DB_PASS, $options);
}
public function model($model)
{
require_once '../app/models/'. $model .'.php';
return new $model($this->_database);
}
public function view($view)
{
require '../app/views/'. $view .'.php';
}
}
class Contact extends Controller
{
public function index()
{
$name = 'David';
$this->view('contact/index');
}
First of all my view function (in Controller.php) didnt work properly:
By echo $name in 'contact/index.php' view i get this error: Undefined variable: name
If i just call require '../app/views/contact/index.php' in Contact controller, index method its works perfectly, but by calling the view method from Controller i get this error.
And the second problem is that i cant get data from Models properly
class User
{
public $_database;
public function __construct($database)
{
try
{
$this->_database = $database;
}
catch (PDOException $e)
{
exit('Database connection could not be established.');
}
}
public function select()
{
$stmt = "SELECT * FROM users WHERE ID = :id";
$query = $this->_database->prepare($stmt);
$query->execute(array(':id' => 1));
return $query->fetchAll(PDO::FETCH_OBJ);
}
}
By calling $name = $this->model('User')->select in Contact Controller and echo $name->Username in 'contact/index' view i get this error: Trying to get property of non-objec
I used PDO::FETCH_OBJ to return an object instead of an array but still dosn't work.
Your select is returning an array of all users. You reduce the result to one with the query, but PDO does not know that.
Use $query->fetch(PDO::FETCH_OBJ) instead.
Your view is loaded (require) in the Controller::view() method. It only has access to the variables, this method itself can access. Therefore it cannot reach $name.
You could change your view method like this:
public function view($view, array $parameters = [])
{
foreach ($parameters as $varname => $value) {
$$varname = $value;
}
require '../app/views/'. $view .'.php';
}
Now you can use it like this:
public function index()
{
$this->view('contact/index', ['name'=>'David']);
}
Create a set function in your Controller for variables you want access to in your view, for example (taken from CakePHP):
public function set($one, $two = null) {
if (is_array($one)) {
if (is_array($two)) {
$data = array_combine($one, $two);
} else {
$data = $one;
}
}
else {
$data = array($one => $two);
}
$this->viewVars = $data + $this->viewVars;
}
Now you can use $this->set('variable', 'value') if you want $variable to be accessible in your view (with the value value).
Then right before you render your view, use extract($this->viewVars); and you'll be able to use $variable in your view.
How to make class structure like PDO or ORM
$query = DB::table('users')->select('name');
$users = $query->addSelect('age')->get();
OR
$stmt = $pdo->prepare($sql);
$stmt->bindvalue(':u',intval($_SESSION['userId']),PDO::PARAM_INT);
$stmt->execute();
What is returned in to $query or $stmt?
how to design class structure like them?
Thank you
EDIT
$query = DB::table('users')->select('name');
meaning :
function select(){
//
return $this;
}
what's returned in to $query for this structure :
$query->addSelect('age')->get();
PDO is done by returning a new class (PDOStatement) with its own methods (read more about it), but it would be the same as:
<?php
class ClassOne
{
private $connection;
public function __construct($database_stuff)
{
$this->connection = $database_stuff;
}
public function prepare($sql)
{
// Code that does something with the $sql
// Then return a new class
return new ClassTwo($this);
}
}
class ClassTwo
{
private $ClassOne;
public function __construct(ClassOne $Class)
{
$this->ClassOne = $Class;
}
public function execute()
{
// Code that does something with ClassOne
}
}
# Start that initial class
$Class = new ClassOne('database:type;host=example;etc=yadayada');
# Do class one method
$query = $Class->prepare("SELECT * FROM fake_table");
# $query is now ClassTwo, so you do method from ClassTwo
$query->execute();
The ability to chain methods together is achieved because the current method returns the object back in the form of $this:
<?php
class DBClass
{
protected $connection,
$value;
public function __construct($connection)
{
$this->connection = $connection;
}
public function prepare($value)
{
$this->value = $value;
# Return the object
return $this;
}
public function execute()
{
echo $this->value;
# Return the object
return $this;
}
}
$con = new DBClass("login creds");
$con->prepare("update stuff if stuff = 'things'")->execute();
?>
I'm trying to write some classes which pull my data out of my database and create objects based on that data. I'm working with CodeIgniter.
The problem is that if an id is supplied to the __construct method but no row in the database table has that id, an object is returned but with all the properties set to NULL.
Is there a way that I can check this and return NULL instead of an object if there is no corresponding row?
class JS_Model extends CI_Model
{
protected $database_table_name;
protected $database_keys;
...
public function __construct($id = NULL){
if($id){
$this->getFromDatabase($id);
}
}
...
function getFromDatabase($id){
foreach($this->database_keys as $key){
$this->db->select($key);
}
$this->db->from($this->database_table_name);
$this->db->where('id', $id);
$this->db->limit(1);
$q = $this->db->get();
if ($q->num_rows() > 0){
foreach($q->result() as $property){
foreach($property as $key => $value){
$this->$key = $value;
}
}
} else {
// NEED TO SET THE OBJECT TO NULL FOR THIS CASE
}
}
...
}
Any help would be appreciated.
This is not possible, once __construct is called, you will receive an object instance.
The correct way to handle this is to throw an Exception from inside the constructor.
class JS_Model extends CI_Model
{
protected $database_table_name;
protected $database_keys;
public function __construct($id = NULL){
if($id){
$row = $this->getFromDatabase($id);
if (!$row) {
throw new Exception('requested row not found');
}
}
}
}
try {
$record = new JS_Model(1);
} catch (Exception $e) {
echo "record could not be found";
}
// from here on out, we can safely assume $record holds a valid record
why dont you make your constructor private and use a method such as GetInstance(); something like this. Syntax may be incorrect as i dont write much php anymore :-(.
class JS_Model extends CI_Model
{
protected $database_table_name;
protected $database_keys;
...
private function __construct($id = NULL){
}
public static function GetInstance($id)
{
$x = new JS_Model($id);
$x->getFromDatabase($id);
if(is_object($x))
{
return $x;
}
return null;
}
...
function getFromDatabase($id){
foreach($this->database_keys as $key){
$this->db->select($key);
}
$this->db->from($this->database_table_name);
$this->db->where('id', $id);
$this->db->limit(1);
$q = $this->db->get();
if ($q->num_rows() > 0){
foreach($q->result() as $property){
foreach($property as $key => $value){
$this->$key = $value;
}
}
} else {
return null;
}
}
...
}
I have the current basic structure for each domain object that I need to create:
class Model_Company extends LP_Model
{
protected static $_gatewayName = 'Model_Table_Company';
protected static $_gateway;
protected static $_class;
public static function init()
{
if(self::$_gateway == null)
{
self::$_gateway = new self::$_gatewayName();
self::$_class = get_class();
}
}
public static function get()
{
self::init();
$param = func_get_arg(0);
if($param instanceof Zend_Db_Table_Row_Abstract)
{
$row = $param;
}
elseif(is_numeric($param))
{
$row = self::$_gateway->find($param)->current();
}
return new self::$_class($row);
}
public static function getCollection()
{
self::init();
$param = func_get_arg(0);
if($param instanceof Zend_Db_Table_Rowset_Abstract)
{
$rowset = $param;
}
elseif(!$param)
{
$rowset = self::$_gateway->fetchAll();
}
$array = array ();
foreach ($rowset as $row)
{
$array[] = new self::$_class($row);
}
return $array;
}
}
I initially tried to refactor the static methods into the parent LP_Model class only to learn finally what "late static binding" means in the php world.
I'm just wondering if anyone has suggestions on how to refactor this code so that I don't have to redeclare the same three functions in every domain object that I create?
How about this:
<?php
abstract class Model_Abstract
{
protected $_gatewayName = null;
protected $_gateway = null;
protected function _init()
{
$this->_gateway = new $this->_gatewayName();
}
protected function __construct($row = null)
{
$this->_init();
if ($row) {
$this->_data = $row;
}
}
public static function getAbstract($class, $param)
{
$model = new $class();
if($param instanceof Zend_Db_Table_Row_Abstract)
{
$row = $param;
}
elseif(is_numeric($param))
{
$row = $model->_gateway->find($param)->current();
}
return new $class($row);
}
public static function getAbstractCollection($class, $param = null)
{
$model = new $class();
if($param instanceof Zend_Db_Table_Rowset_Abstract)
{
$rowset = $param;
}
elseif($param === null)
{
$rowset = $model->_gateway->fetchAll();
}
$array = array ();
foreach ($rowset as $row)
{
$array[] = new $class($row);
}
return $array;
}
abstract public static function get($param);
abstract public static function getCollection($param = null);
}
class Model_Company extends Model_Abstract
{
protected $_gatewayName = 'Model_Table_Company';
public static function get($param) {
return self::getAbstract(__CLASS__, $param);
}
public static function getCollection($param = null) {
return self::getAbstractCollection(__CLASS__, $param);
}
}
class Model_Table_Company extends Zend_Db_Table_Abstract
{
protected $_name = 'company';
}
$model = Model_Company::get(1);
print "Got an object of type ".get_class($model)."\n";
$models = Model_Company::getCollection();
print "Got ".count($models)." objects of type ".get_class($models[0])."\n";
?>
Unfortunately, to make the functions easy to call, you have to duplicate get() and getCollection() in each subclass. The other option is to call the function in the parent class:
$model = Model_Abstract::getAbstract('Model_Company', 1);
print "Got an object of type ".get_class($model)."\n";
$models = Model_Abstract::getAbstractCollection('Model_Company');
print "Got ".count($models)." objects of type ".get_class($models[0])."\n";
You can rename the base class and its function names if you want to go that route. But the point is that you must name the child class in one place or the other: either make a boilerplate function in the child class as in my first example, or else name the class in a string as in my second example.