I'm using the Zend_Db framework, and I've run into a snag. I'm trying to add custom handling to the column naming at the row level, but it's failing to invoke my function for some reason.
I've stripped down the problem to simply try and figure out if the underlying "Row" class is ever even created. From what I can tell, it isn't.
Here's what I've got:
// this class functions correctly; I get "table" written to my output
class DHR_Table extends Zend_Db_Table_Abstract
{
protected $_rowClass = 'DHR_Row';
function __construct(){
echo "table";
parent::__construct();
}
}
// this class never gets called, at least not that is evident from the constructor echo
class DHR_Row extends Zend_Db_Table_Row_Abstract
{
protected $inflector = null;
function __construct(){
echo "row";
parent::__construct();
}
}
// this is the actual implementation class that uses these two:
class Application_Model_DbTable_Applicants extends DHR_Table
{
protected $_name = 'applicants';
}
My output includes some data (excluded from this post, but part of the "Applicants" class) and "table", but no "row". Any ideas why this might be happening? Version 1.11.11 of the Zend framework.
[Edit]
Here's the usage:
class ApplicantsController extends DHR_RestController
{
public function indexAction()
{
$applicants = new Application_Model_DbTable_Applicants();
$result = $applicants->fetchAll();
$this->success($result);
}
protected function success($data, $code = 200)
{
if(is_a($data, 'Zend_Db_Table_Rowset')){
// we could do some pagination work here
$data = $data->toArray();
}
$this->setResponseCode($code)->appendBody(Zend_Json::encode(array(
'success'=>true,
'data' => $data
)));
}
}
I would expect to at least have some method on the row class invoked when returning the serialized results...
[Update]
If I use "fetchRow" everything works as expected; fetchAll simply does't do the conversion to the underlying object type.
I was just looking at the code for the row/abstract class.
Try setting a value for $_tableClass. $_tableClass = 'DHR_Table';
I'm afraid that won't work as it looks like Zend/Db/Table/Row/Abstract.php is going to look for a table definition no matter what, so the level of abstraction you seem to be after may not be possible without further extending.
//excerpt from __construct Zend/Db/Table/Row/Abstract.php
public function __construct(array $config = array())
{
if (isset($config['table']) && $config['table'] instanceof Zend_Db_Table_Abstract) {
$this->_table = $config['table'];
$this->_tableClass = get_class($this->_table);
} elseif ($this->_tableClass !== null) {
$this->_table = $this->_getTableFromString($this->_tableClass);
}
// cont...
// Retrieve primary keys from table schema
if (($table = $this->_getTable())) {
$info = $table->info();
$this->_primary = (array) $info['primary'];
}
$this->init();
Related
I keep seeing this error -
This is the offending function -
private function getInfo(){
$this->Features = new UserFeatures_Model($this->ID); //<-- Offending line of Code
/*Other Stuff - Not Relevant*/
}
This is from where it's being called -
public function __construct($UserID = NULL){
parent::__construct( TRUE );
$this->database = $this->load->database('users', TRUE);
$this->table = 'users';
$this->idKey = 'User_ID';
//Assigned UserID should have precedence.
if (!is_null($UserID)) { $this->ID = $UserID; }
//If there exists a UserID within this session, automatically load it.
elseif ($this->session->UserID){ $this->ID = $this->session->UserID; }
if (isset($this->ID)){ $this->getInfo(); }
}
My first guess as to why this is throwing a fit is because I'm calling the getInfo() method in the constructor... but I'm pretty sure that's not the issue.
Anyway, here is the class definition that it is saying does not exist -
class UserFeatures_Model extends MY_Model {
/*Irrelevant stuff since it's not 'seeing' this class anyway...*/
}
And here is the directory structure -
It's there unless I'm hallucinating (a distinct possibility, I am quite tired...)
Why is(are) CI/PHP not finding this class?
You must use $this->load->model('UserFeatures_Model'); inside your getInfo() controller before you call that model function.
private function getInfo($id){
$this->load->model('UserFeatures_Model');
$this->Features = $this->UserFeatures_Model->some_function_inside_model($id);
}
How exactly does CI custome object works ?
As per CI documentation You can also pass a string to result() which represents a class to instantiate for each result object (note: this class must be loaded)
$query = $this->db->query("SELECT * FROM users;");
foreach ($query->result('User') as $row)
{
echo $row->name; // call attributes
echo $row->reverse_name(); // or methods defined on the 'User' class
}
}
This is a very nice feature yet what Ci does is it will return an array of User objects and set attributes from row to it.
i have a problem with it that i want to have more control on what attributes to be publicly accessed and what to be modified before setting/getting.
how can i accomplish this ? can i tell CI to pass all attributes to constructor so that class can populate its own data ?
example class User
class User{
private $data=array();
protected $CI;
//public $id,$name,$dob,$gender,$role,$username,$password,$salt,$picture,$lastactive;
function __construct($data=null)
{
$this->data = $data; // i want to save data to a private var and allow attr. throu getters only
}
function set_password($p){
$this->generateSalt();
$this->data->password = $p.$this->data->salt;
}
}
In a nutshell::
I want to use custom_result_object but i dont want codeigniter to populate class attributes for me, instead i want the class to receive those attrs and populate it him self the way he this its appropriate.
I found your question while looking for a solution for myself.
After digging a bit in the documentation I managed to figure it out:
class user_item {
// you can declare all the attributes you want as private
private $id,$name,$dob,$gender,$role,$username,$password,$salt,$picture,$lastactive;
function __construct(){
// you can use the constructor to format data as needed
$this->username = strtouppper($this->username);
}
public function set_password($p){
$this->generateSalt();
$this->password = $p.$this->salt;
}
public function get_password(){
return $this->password;
}
}
Once set up, you can instantiate this class from $this->db->result()
class User_model extends CI_Model {
public function get_user($id){
return $this->db->get_where('users', array('id' => $id), 1)->result('user_item');
}
}
And call any public method or attribute of the class as needed
class Users extends CI_Controller {
function __construct(){
$this->load->model('user');
}
public function profile($user_id){
var $myUser = $this->user->get_user($user_id);
$myUser->set_password('myPassword');
echo $myUser->get_password();
}
}
I have simplified the code to make it clearer, but you get the idea.
this example controller using result array and object
if ($this->session->userdata('id_jurusan') ==1) {
$where=array('id_jurusan'=>$this->session->userdata('id_jurusan'));
$value = $this->session->userdata('id_jurusan');
$value2 = $this->session->userdata('username');
$data['rule']=$this->guru_mod->get_where($where,'forward_changing')->result();
$data['fuzzy']=$this->guru_mod->get_data_all('fuzzy')->result();
$data['artikel']=$this->guru_mod->get_data_all('artikel')->result();
$data['kondisi']=$this->guru_mod->get_where($where,'kondisi')->result();
$data['artikel2'] = $this->guru_mod->get_data_all2('artikel','id_jurusan',$value);
$data['riwayat_rule'] = $this->guru_mod->get_data_all2('forward_changing','username',$value2);
$data['kondisi_rule'] = $this->guru_mod->get_data_all2('kondisi','id_jurusan',$value);
$this->load->view('guru/daftar_rule',$data);
}
If we have a code like this:
class Game {
private $_id;
private $_name;
private $_url;
public function __construct($_id,$_name,$_url){
$this->_id = $_id;
$this->_name = $_name;
$this->_url = $_url;
}
}
And we want to simply connect to our Database to get a game by id, where do we place the 'getByID' function?
Do we place it within the 'Game Class' as 'static function', do we put it in the 'Database Connection Class' as 'public function' or do we just put the method in the 'general functions inside the main index.php' as 'function'?
I currenctly have choosen for a 'static function' within the 'Game Class':
public static function getByID($id,$db){
$query = "SELECT * FROM game WHERE id = :id LIMIT 1";
$prepare = array(":id"=>$id);
$result = $db->Precute($query,$prepare);
foreach($result as $r) return new Game($r['id'],$r['name'],$r['url']);
return null;
}
(Precute is a custom function within the Database Class to prepare and execute the query)
How would you approach this?
In proper OOP, a DAL function which returns an instance of a specific class should be static within that class. As a base rule, all functionality related to one specific object should be part of that specific object, as an instance method if invoked on instances or a static method if it creates or manages instances ('factory pattern').
Your function isn't static currently, correct usage would be:
class Game
{
..other functions..
public static function getById($id)
{
..implementation, which can either access central storage or retrieve
the object itself if concurrent edits are not an issue..
}
}
Then elsewhere:
$myGame = Game::getById(684);
You may want to have a look at Doctrine instead of re-inventing the wheel. And even if you do want to make a new wheel, its code samples all follow correct OOP principles.
This Answer takes another approach. Instead of getting Objects from Static Factory. This solution takes a approach of creating a blank object and then calling the database methods to make the object a live representation of a actual row.
first the observations from your question -
an Object/Instance of Game class represents a Row of Table game. And the Game class itself can be taken as a representation of `game' table.
If the above observation is correct along with the assumption that there are more tables with a representation in class hierarchy. You should have a class to represent generic 'Table'
class Table { //The class itself can be made abstract depending upon the exact implementation
protected $_tableName;
protected $_connectionParams;
protected $idAttribute = 'id';
public function __construct($tableName, $connectionParams, $idAttribute){
$this->_connectionParams = $connectionParams;
$this->_tableName = $tableName;
if(isset($idAttribute)) {
$this->idAttribute = $idAttribute;
}
};
private function _getConnection() {
//return $db using $_connectionParams
};
public function getByID($id) {
$this->getByKeyVal($this->idAttribute, $id);
};
public function getByKeyVal($key, $val) {
$query = "SELECT * FROM ". $this->_tableName ." WHERE `". $key ."` = :key LIMIT 1";
$prepare = array(":key"=> $val);
$result = $this->_getConnection()->Precute($query,$prepare);
$this->processRow($result[0]);
};
//This needs to be overridden
public function processRow($row) {
return true;
};
}
Now extend the generic Table class for Game Table
class Game extends Table {
private $_id;
private $_name;
private $_url;
public function __construct($defaults) {
if(isset($defaults) {
if(is_array($defaults)) {
$this->processRow($defaults);
} else {
$this->getByID($defaults);
}
} else {
//Some default setup here if requried
}
$connectionParams = []; //Prepare Connection Params here
parent::__construct('game', $connectionParams);
};
//Override processRow
public function processRow($row) {
if(isset($row['id']) {
$this->_id = $row['id'];
}
$this->_name = $row['name'];
$this->_url = $row['url'];
};
}
Above is a very rough example. The actual Class structure will depend upon your requirements. But the general rule of thumb is to treat a Class as a blueprint of a concrete object. And all the methods related with a Generic Classification should go in there own class.
The getConnection Method itself can be put into a seprate DB connection class and inserted in table via a either mixin pattern or generic class inheritance.
Use the above setup like this
$game_new = new Game(); // for blank object --- for a new row
$game_435 = new Game(435); //row with 435 ID
$game_default = new Game(array( //new row with defaults
'name' => 'Some Name',
'url' => 'Some Url'
));
What you want is a "bucket" full of Game objects. When ever you want a Game Object (representing data in your database), you ask your "bucket" to give it to you. Let me give you an example of how Doctrine2 implements this:
http://docs.doctrine-project.org/en/2.0.x/reference/working-with-objects.html
So where you want to place your "getById" (or as I would do "findById"), is in your "bucket".
// lets presume that the em is an instance of \Doctrine\ORM\EntityManager
// The entity manager does what the name says.
$id = 1234;
$game = $entity_manager->find('MyNamespace\Entity\Game', $id);
$game->setName('My first game!');
// We now tell the em to prepare the object for pushing it back to the "bucket" or database
$entity_manager->persist($game);
// Now we tell the em to actually save stuff
$entity_manager->flush();
This should give you an indication of how to use it. Objects follow the Single Responsibility Principle. You don't ask an object to retrieve itself. You ask the "bucket" to retrieve you an Object.
http://en.wikipedia.org/wiki/Single_responsibility_principle
What if I told you that there are more beautiful ways to put things on their places.
A very simple case might contain 3 basic components to work:
Db framework - Which handles data access.
Table repsotor classes - Which know how to map classes to tables,
how to create classes from table data and how to create data from table classes.
Model or business layer which contain actual classes.
For better understanding imagine you have database object mapper framework.
The framework can be far complex but in few lines we can demonstrate how it`s basic
concepts work.
So the 'Framework':
<?php
//This class is for making link for db framework
class link
{
public $link;
public function __construct ($hostname, $database, $gamename, $password)
{
$this->link = new \PDO ('mysql:host='.$hostname.';dbname='.$database, $gamename, $password);
$this->link->query('use '.$database);
}
public function fetch ($query)
{
$result = $this->link->query($query)->fetch();
}
public function query ($query)
{
return $this->link->query($query);
}
public function error ()
{
return $this->link->errorInfo();
}
}
//This class collects table repositories and connections
class database
{
public $link;
public $tables = array ();
public function __construct ($link)
{
$this->link = $link;
table::$database = $this;
}
}
//This is basic table repositor class
class table
{
public static $database;
}
?>
Now as we have our db framework let us make some table repositor which knows
how to save/load/delete game:
class games extends table
{
public function create ($row)
{
$return = new game ();
$return->id = $row[0];
$return->name = $row[1];
var_export($row);
return $return;
}
public function load ($id=null)
{
if ($id==null)
{
$result = self::$database->link->fetch("select * from games");
if ($result)
{
$return = array();
foreach ($result as $row)
{
$return[$row[0]] = $this->create($row);
}
return $return;
}
}
else
{
$result = self::$database->link->fetch("select * from games where id='".$id."'");
if ($result)
{
return $this->create(reset($result));
}
else
{
echo ("no result");
}
}
}
public function save ($game)
{
if (is_array($save))
{
foreach ($save as $item) $this->save ($item);
}
if ($game->id==null)
{
return self::$database->link->query("insert into games set
name='".$game->name."'");
}
else
{
return self::$database->link->query("update games set name='".$game->name."'
where id='".$game->id."'");
}
}
public function delete ($game)
{
self::$database->link->query ("delete from games where id='".$game->id."'");
}
}
Now we can make our model which in this case will contain actuall game class.
class game
{
public $id;
public $name;
public function __construct ($name=null)
{
$this->name = $name;
}
}
And than actually use it:
$database = new database (new link('127.0.0.1', 'system_db', 'root', '1234'));
$database->tables['games'] = new games();
if (!$database->tables['games']->save (new game('Admin')))
{
var_export($database->link->error());
}
var_export($database->tables['games']->load(2));
For the moment I prefere this pattern for working with db in my projects. Using it I can achieve
that my actuall business objects(In this case class game) will know nothing about
where and how they are saved. This gives me an ability to be indipendent from
actuall storage and focus on project logics.
Also there is one lightweight framework so called db.php (http://dbphp.net) and it even
gives me ability to avoid to write table repositories and even creates/modifies tables
needed for my business classes on the fly but uses almost same concept I described here.
I have a PHP MVC application using Zend Framework. As presented in the quickstart, I use 3 layers for the model part :
Model (business logic)
Data mapper
Table data gateway (or data access object, i.e. one class per SQL table)
The model is UML designed and totally independent of the DB.
My problem is : I can't have multiple instances of the same "instance/record".
For example : if I get, for example, the user "Chuck Norris" with id=5, this will create a new model instance wich members will be filled by the data mapper (the data mapper query the table data gateway that query the DB). Then, if I change the name to "Duck Norras", don't save it in DB right away, and re-load the same user in another variable, I have "synchronisation" problems... (different instances for the same "record")
Right now, I use the Multiton / Identity Map pattern : like Singleton, but multiple instances indexed by a key (wich is the user ID in our example). But this is complicating my developpement a lot, and my testings too.
How to do it right ?
Identity Map
Edit
In response to this comment:
If I have a "select * from X", how can I skip getting the already loaded records ?
You can't in the query itself, but you can in the logic that loads the rows into entity objects. In pseudo-code:
class Person {}
class PersonMapper {
protected $identity_map = array();
function load($row) {
if (!isset($this->identity_map[$row['id']])) {
$person = new Person();
foreach ($row as $key => $value) {
$person->$key = $value;
}
$this->identity_map[$row['id']] = $person;
}
return $this->identity_map[$row['id']];
}
}
class MappingIterator {
function __construct($resultset, $mapper) {
$this->resultset = $resultset;
$this->mapper = $mapper;
}
function next() {
$row = next($this->resultset);
if ($row) {
return $this->mapper->load($row);
}
}
}
In practice, you'd probably want your MappingIterator to implement Iterator, but I skipped it for brevity.
Keep all loaded model instances in "live model pool". When you load/query a model, first check if it has been already loaded into pool (use primary key or similar concept). If so, return the object (or a reference) from pool. This way all your references point to the same object. My terminology may be incorrect but hopefully you get the idea. Basically the pool acts as a cache between business logic and database.
Multiton
Best option if you want to use a variety of singletons in your project.
<?php
abstract class FactoryAbstract {
protected static $instances = array();
public static function getInstance() {
$className = static::getClassName();
if (!(self::$instances[$className] instanceof $className)) {
self::$instances[$className] = new $className();
}
return self::$instances[$className];
}
public static function removeInstance() {
$className = static::getClassName();
if (array_key_exists($className, self::$instances)) {
unset(self::$instances[$className]);
}
}
final protected static function getClassName() {
return get_called_class();
}
protected function __construct() { }
final protected function __clone() { }
}
abstract class Factory extends FactoryAbstract {
final public static function getInstance() {
return parent::getInstance();
}
final public static function removeInstance() {
parent::removeInstance();
}
}
// using:
class FirstProduct extends Factory {
public $a = [];
}
class SecondProduct extends FirstProduct {
}
FirstProduct::getInstance()->a[] = 1;
SecondProduct::getInstance()->a[] = 2;
FirstProduct::getInstance()->a[] = 3;
SecondProduct::getInstance()->a[] = 4;
print_r(FirstProduct::getInstance()->a);
// array(1, 3)
print_r(SecondProduct::getInstance()->a);
// array(2, 4)
Ok this is not just a PHP question, but I have this problem in PHP so I will post the example in PHP.
Example:
I have three classes, say, data_abstract, person, and student. Now, I have one array of data related to each of those objects.
class person extends data_abstract
{
protected $data; //name, gender, etc
}
class student extends person
{
protected $data; //student_id, etc
}
Now, assuming each of those "data" property is from database tables, for example, table_person, table_student.
Upon instantiation, we will parse the class name by get_class() function, and get data from their related tables.
class data_abstract
{
public function __construct()
{
$name = get_class($this);
$table = 'table_' . $name;
$this->data = DB->query('SELECT * FROM ' . $table);
//whatever DB constant is,
//I'm just trying to get all rows of data from the related table.
}
}
Ok, now the problem, when I instantiate student by $student = new student(); the constructor will get data from table_student for me and put it in $student->data, but I won't be able to get the data from table_person and put those two sets of data into one object.
By extending another class, we can have all the methods (functions) extended and customized (via polymorphism), but extending the attributes/properties of each level of object seems to be hard, at least without some manual construction.
Is there any way in the abstraction level to achieve this?
(thanks for reading, wish I could've made the question clearer.)
If I understand well :
you want all your DB data in $data;
$data must be feed with data from the table 'table_classname';
$data "inherit" from $data that should have been loaded in parents class with that process.
In that case, you should externalize $data feeding so you can overload the data feeding. I edited, now we have a working example :
class DataAbstract // Using caps is much better for class name
{
public function __construct()
{
$this->loadData();
}
public function loadData()
{
// don't use getclass on $this or $this will refer to the children
$table = 'table_' . __CLASS__;
$this->data = array($table);
}
}
class Person extends DataAbstract
{
public function __construct()
{
parent::__construct();
}
public function loadData()
{
parent::loadData();
$table = 'table_' . __CLASS__;
$this->data = array_merge($this->data, array($table));
}
}
class Student extends Person
{
public function __construct()
{
parent::__construct();
}
public function loadData()
{
parent::loadData();
$table = 'table_' . __CLASS__;
$this->data = array_merge($this->data, array($table));
}
}
$c = new student();
print_r($c->data);
Outputs
Array
(
[0] => table_DataAbstract
[1] => table_Person
[2] => table_Student
)
BTW, remember that PHP got introspection, it allows you you to set the fields you need dynamically : maybe cleaner that using a big Array.
Provided you know all the fields names, you can do something like
function populate($data) // get the array from the DB
{
$fieldList = get_class_vars($this); // get the filed list
foreach ($fieldList as $key => $value)
{
$this->$key = $data[$key]; // feed the field one by one with the array
}
}
Personally, I would put the database loading class in an protected method that takes the attribute $tablename, and which loads all the data. Then in the constructor of the class call it by hand, and the parent.
class Person
{
public function __construct() {
$this - > loadData("Person");
}
}
Class Student extends Person
{
public function __construct() {
parent::__construct();
$this - > loadData("Student");
}
}
Now, when you construct Student both data from Student and Person is loaded. if you use array_merge() the 2 data arrays will be merged into one.
It's a matter of style; some people would critisise this by saying that you are now repeating something twice - more work to refactor - but I would say you are decoupling the class names and interfaces from the database design (a good thing) and with the parent call, the code is now more OO.