Can I make PDOStatement->fetchObject not use non-member variables? - php

Lets say I have a class like this:
Class User {
var $id
var $name;
}
And I run a query using PDO in php like so:
$stm = $db->prepare('select * from users where id = :id');
$r = $stm->execute(array(':id' => $id));
$user = $r->fetchObject('User');
If I vardump my user object it has all kinds of other fields in it that I have not defined in the User class. Obviously I could make my query specific so that it only gives me back the fields I need/want. But if I don't want to do that is there any way to make this work the way I want it to?
I like the idea of fetchObject, because it's one line of code to create this object and set member variables for me. I just don't want it to set variables I haven't defined in my class.
EDIT:
Well it seems like karim79 is right and the fetch or fetchObject won't work the way I want it to. I've added the following bit of code after I do the fetch to get the desired results.
$valid_vars = get_class_vars('User');
foreach (get_object_vars($user) as $key => $value) {
if (!array_key_exists($key, $valid_vars)) {
unset($user->$key);
}
}
Obviously not the most elegant solution :/ I'm going to extend the PDOStatement class and add my own method fetchIntoObject or something like that and automatically do these unsets. Hopefully shouldn't be to much overhead, but I want to be able to easily fetch into an object with 1 line of code :)
SUPER EDIT:
Thanks to mamaar's comment I went back to the documentation again. I found what the problem is. http://us.php.net/manual/en/pdo.constants.php and scroll down to PDO::FETCH_CLASS and it explains that the magic method __set() is used if properties don't exist in the class. I overwrote the method in my target class and tada, works. Again, not the most elegant solution. But now I understand the WHY, and that's important to me :D

PDOStatement->execute() does not return an object - it returns TRUE/FALSE.
Change lines 2 and 3 to
if ( $stm->execute( array( ':id' => $id ) ) ){
$user = $stm->fetchObject( 'User' );
}
and it works

I don't think that's possible. fetchObject will create an instance of the classname specified as fetchObject's $class_name parameter (which defaults to stdClass). It will not check for existing classes with the same name and create an instance, assigning values only to member variables which match column names in the result. I would suggest relying on something more boring, like this:
$user = new User($result['id'], $result['name']);
Which would of course mean giving your User class a constructor:
Class User {
var $id
var $name;
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
}

You could probably use the PDOStatement->fetch method with PDO::FETCH_CLASS or PDO::FETCH_INTO as the $fetch_style parameter
Edit: So I've tried myself, and got it to work with PDOStatement->setFetchMode
class User
{
public $id;
public $name;
}
$db = new PDO('mysql:host=127.0.0.1;dbname=test', 'username', 'password');
$stmt = $db->prepare("select * from users where id=:userId");
$stmt->setFetchMode(PDO::FETCH_CLASS, 'User');
$stmt->execute(array(':userId' => 1));
$user = $stmt->fetch();
var_dump($user);

As alternative, you can of course just fetch an array and simply typecast this yourself:
$user = (User) $r->fetch();
Btw, I've not seen this behaviour. Maybe you have PDO::FETCH_LAZY activated, that might create extra data. You could test it with ->fetchObject("stdClass"), else the reason resides with your User class, or its Parent?

Related

how i can make find method does not make a problem

i create a Depot class. when i create object from this class i use find method for find a Special item with id.
after that i cant call any other method.
I do not use Laravel
// index.php file
$depot = new Depot();
$depot = $depot->find(2);
var_dump($depot->hi());
Fatal error: Uncaught Error: Call to undefined method stdClass::hi()
hi method is for test.
// model.php file
class Model {
// ...
public function find(int $id)
{
$statement = $this->pdo->prepare("select * from {$this->table} where id = :id");
$statement->execute(compact('id'));
$obj = $statement->fetch(PDO::FETCH_OBJ);
return $obj;
}
}
class Depot extends Model {
//...
public function hi()
{
echo "hi";
}
}
With this line:
$depot = $depot->find(2);
you're overwriting the variable $depot, representing your object, with the result of your query. The object returned (unsurprisingly) doesn't contain a function called hi().
I don't know if this was just a typo, but if not, it's generally a sign of poor code quality if you re-use the same variable to contain two completely different things. It leads to maintenance and readability issues, and often causes errors further down the line, such as this one, where you mistakenly assume the variable still has its original content. Weakly-typed languages such as PHP are especially vulnerable to this kind of mistake. The easiest thing is to just make a rule never to do it.
Assigning the result to a different variable, e.g.
$depot = new Depot();
$findResult = $depot->find(2);
$depot->hi();
will fix the issue.
(Also the var_dump() was unnecessary since hi() already contains an echo.)
try this
$depot = new Depot();
$depotDb = $depot->find(2);
var_dump($depot->hi());

How can I make an array of type "class" in PHP?

I have the following class with several properties and a method in PHP (This is simplified code).
class Member{
public $Name;
public $Family;
public function Fetch_Name(){
for($i=0;$i<10;$i++){
$this[$i]->$Name = I find the name using RegExp and return the value to be stored here;
$this[$i]->Family = I find the family using RegExp and return the value to be stored here;
}
}//function
}//class
In the function Fetch_Name(), I want to find all the names and families that is in a text file using RegExp and store them as properties of object in the form of an array. But I don't know how should I define an array of the Member. Is it logical or I should define StdClass or 2-dimension array instead of class?
I found slightly similar discussion here, but a 2 dimensional array is used instead of storing data in the object using class properties.
I think my problem is in defining the following lines of code.
$Member = new Member();
$Member->Fetch_name();
The member that I have defined is not an array. If I do define it array, still it does not work. I did this
$Member[]= new Member();
But it gives error
Fatal error: Call to a member function Fetch_name() on a non-object in
if I give $Member[0]= new Member() then I don't know how to make $Member1 or Member[2] or so forth in the Fetch_Name function. I hope my question is not complex and illogical.
Many thanks in advance
A Member object represents one member. You're trying to overload it to represent or handle many members, which doesn't really make sense. In the end you'll want to end up with an array that holds many Member instances, not the other way around:
$members = array();
for (...) {
$members[] = new Member($name, $family);
}
Most likely you don't really need your Member class to do anything really; the extraction logic should reside outside of the Member class, perhaps in an Extractor class or something similar. From the outside, your code should likely look like this:
$parser = new TextFileParser('my_file.txt');
$members = $parser->extractMembers();
I think you should have two classes :
The first one, Fetcher (or call it as you like), with your function.
The second one, Member, with the properties Name and Family.
It is not the job of a Member to fetch in your text, that's why I would make another class.
In your function, do your job, and in the loop, do this :
for($i = 0; $i < 10; ++$i){
$member = new Member();
$member->setName($name);
$member->setFamily($family);
// The following is an example, do what you want with the generated Member
$this->members[$i] = $member;
}
The problem here is that you are not using the object of type Member as array correctly. The correct format of your code would be:
class Member{
public $Name;
public $Family;
public function Fetch_Name(){
for($i=0;$i<10;$i++){
$this->Name[$i] = 'I find the name using RegExp and return the value to be stored here';
$this->Family[$i] = 'I find the family using RegExp and return the value to be stored here';
}
}
}
First, $this->Name not $this->$Name because Name is already declared as a member variable and $this->Name[$i] is the correct syntax because $this reference to the current object, it cannot be converted to array, as itself. The array must be contained in the member variable.
L.E: I might add that You are not writing your code according to PHP naming standards. This does not affect your functionality, but it is good practice to write your code in the standard way. After all, there is a purpose of having a standard.
Here you have a guide on how to do that.
And I would write your code like this:
class Member{
public $name;
public $family;
public function fetchName(){
for($i=0;$i<10;$i++){
$this->name[$i] = 'I find the name using RegExp and return the value to be stored here';
$this->family[$i] = 'I find the family using RegExp and return the value to be stored here';
}
}
}
L.E2: Seeing what you comented above, I will modify my answer like this:
So you are saying that you have an object of which values must be stored into an array, after the call. Well, after is the key word here:
Initialize your object var:
$member = new Memeber();
$memebr->fechNames();
Initialize and array in foreach
$Member = new Member();
foreach ($Member->Name as $member_name){
$array['names'][] = $member_name;
}
foreach ($Member->Family as $member_family) {
$array['family'][] = $member_family;
}
var_dump($array);
Is this more of what you wanted?
Hope it helps!
Keep on coding!
Ares.

Cast codeigniter database result object to custom object

Codeigniter can return a database query as generic "Object" like:
$q = $this->db->get("some_table");
$obj = $this->q->row();
$var = $obj->some_property
In my case I want to make a PHP class who's public variables are 1 for 1 with the database columns, along with some public methods. Is there a quick one-shot way to cast or convert the generic "Row" object into my custom class object? I've read posts that hint that it is certainly possible, but most involve a really hacky serialize/deserialize solution. In the past I have just done:
public function __construct($row) {
$this->prop = $row->prop;
$this->id = $row->id;
$this->value = $row->value;
}
And I find this is very tedious and makes ugly code.
See the third section under result():
CodeIgniter User Guide: Generating Query Results
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
}

Passing $db object to other classes so they can access the database

I've got a PHP database class which connects to MySQL and wraps up all the PDO code and I use it to query the database. Basically in the page controller I make a new object:
$db = new Database($dbConfig);
Then I can get data from the database like so using a prepared query:
$params = array('username' => $username);
$result = $db->preparedSelect('select password, salt from users where username = :username', $params);
Which copies the PDO statement results into a new assoc array and returns just the database results back to the calling page. I iterate through them with a simple foreach like so:
foreach ($result as $key => $val)
{
$password = $val['password'];
$salt = $val['salt'];
}
Ok so lets say I want another class to use my $db object so it can access the database in some of the methods. At the moment the other class looks like this:
class General
{
// Database object
private $db;
public function __construct($db)
{
$this->db = $db;
}
}
That works well but I'm just wondering if the constructor should look like this:
public function __construct(&$db)
{
$this->db = $db;
}
That should mean I'm passing it in via reference and not copying the object into the other class. I don't want a copy of the $db object inside the class, I want it to use the existing database object so I don't have multiple copies of it floating around using up memory.
Is there any difference in PHP5 between passing it in as $db or &$db? From doing some reading, PHP5 by default passes objects by reference, and other people saying it now does it the Java way and some say using the & makes a hard link whatever that is. I'm confused. What's the best way to do it?
Many thanks!
There is a difference, but it's not really the difference you may think.
In PHP5, "$db" holding an object is basically equivalent to a "Foo *" in C or C++. In other words, $db doesn't store the whole object, it just stores a small token that lets the code find the object when necessary. When you pass this token by value, it's as fast as passing an integer value rather than a copy of the entire object. But if you assign $db, it doesn't change the value in the caller because you're changing your local variable holding the token to contain a different token.
If the function takes "&$db", that's basically the equivalent of passing "Foo **" in C, or more correctly a function taking a "Foo *&" in C++. The call is just as fast since it's the same size thing that's being passed, but inside the function if you assign to $db it will change the value of $db in the caller because the "pass by reference" variable points you to the memory location holding the token in the caller.
The best way to do it is to pass by value (do not use "&") unless you know what you're doing and why you're doing it.
That's a good question.
You can always do a test by opening a $db handle, passing it to a function, and checking them via the === operator to make sure they are the same object.
This would be a good job for static methods. That is how many frameworks accomplish the same task.
class DB
{
private static $db = FALSE:
public static function init($dbConfig)
{
if(! self:$db)
{
self::$db = new Database($dbConfig);
}
}
public static function preparedSelect($sql, $params)
{
if(! self::$db)
{
die("call the init method first");
}
// db stuff, where you would call $this->db call self::$db
}
}
So in your other classes where you want to make calls to the database all you would have to do is:
class General
{
public function __construct()
{
DB::init($dbConfig);
}
public function someMethod()
{
$params = array('username' => $username);
$result = DB::preparedSelect('select password, salt from users where username = :username', $params);
}
}

PHP custom object casting

I have a custom class object in PHP named product:
final class product
{
public $id;
public $Name;
public $ProductType;
public $Category;
public $Description;
public $ProductCode;
}
When passing an object of this class to my Data Access Layer I need to cast the object passed into a type of the product class so I can speak to the properties within that function. Since type casting in PHP works only with basic types what is the best solution to cast that passed object?
final class productDAL
{
public function GetItem($id)
{
$mySqlConnection = mysql_connect('localhost', 'username', 'password');
if (!$mySqlConnection) { trigger_error('Cannot connect to MySql Server!'); return; }
mysql_select_db('databaseName');
$rs = mysql_query("SELECT * FROM tblproduct WHERE ID='$id';");
$returnObject = mysql_fetch_object($rs, 'product');
return $returnObject;
}
public function SaveItem($objectToSave, $newProduct = false)
{
$productObject = new product();
$productObject = $objectToSave;
echo($objectToSave->Name);
$objectToSave->ID;
}
}
Right now I am creating a new object cast as a type of product and then setting it equal to the object passed to the function. Is there a better way of accomplishing this task? Am I going about the wrong way?
EDITED FOR CLARITY - ADD FULL PRODCUTDAL CLASS
You don't need to cast the object, you can just use it as if it was a product.
$name = $objectToSave->Name;
I´m not sure what you are trying to achieve, but if $objectToSave is already of class product:
You can simply call $objectToSave->SaveItem() (assuming SaveItem() is part of the product class) and access it´s properties in the function like $this->Name, etc.;
In your code $productObject and $objectToSave will hold a reference to the same object.
Type casts in PHP are done like this:
$converted = (type) $from;
Note, that this won't work if the object types are not compatible (if for example $form happens to be a string or object of mismatching type).
But usual solution (called Active Record pattern, present for example in Zend Framework) is to have a base class for a database item called Row. Individual items (for example the class product from your sample) then inherit from this class.
Typical ZF scenario:
$table = new Product_Table();
$product = $table->find($productId); // load the product with $productId from DB
$product->someProperty = $newPropertyValue;
$product->Save(); // UPDATE the database
Which is IMO much better than your solution.
EDIT:
You can't cast between two unrelated objects, it is not possible.
If you want to use the DAL like this, skip the "product" object and go for simple associative array. You can enumerate over its members with foreach, unlike object's properties (you could use reflection, but that's overkill).
My recommendation: Go for the Active Record pattern (it is easy to implement with magic methods). It will save you a lot of trouble.
Currently, you are creating a new Product, then discarding it immediately (as its reference is replaced by $objectToSave.) You will need to copy its properties one by one, I regret.
foreach (get_object_vars($objectToSave) as $key => $value)
{
$product->$key = $value;
}
(If the properties of $objectToSave are private, you will need to a expose a method to_array() that calls get_object_vars($this).)

Categories