How do people usually turn mysqli results into objects?
I cannot find any examples with fetch_object() when using custom classes. Assume the following setup
class Test1 {
function __construct(array $data) { ... }
}
class Test2 {
function __construct(array $data) { ... }
}
Assume that I have multiple classes with the same constructors, how can I dynamically instantiate these classes?
I would imagine something like this:
function customQuery($query, $class) {
// Get some data by mysqli query, assume we get the data as $result object
...
// Now I would like to store the data in an array with instances of $class
$array = array();
while ($obj = $result->fetch_object($class, ???) {
$array[] = $obj;
}
// Return the array with instances of $class
return $array;
}
What do I use as arguments there for my question marks? I only know that both class constructors of Test1 and Test2 want an associative array as input (probably not the same length!).
In the end I simply want to do something like
$arr1 = customQuery('SELECT id, product FROM test1 LIMIT 10', 'Test1');
$arr2 = customQuery('SELECT id, name, address FROM test2 LIMIT 10', 'Test2');
Of course I would appreciate your input if you have better ideas to achieve my goal.
Take a look at "fetch_object'
You have a argument $class_name
From the docs:
The name of the class to instantiate, set the properties of and return. If not specified, a stdClass object is returned.
It will automaticly create an instance of the given class and it will set the properties. so no need to pass an array
http://www.php.net/manual/en/mysqli-result.fetch-object.php
A bit more explanation,
The fetch_object just fills the private properties etc (PHP Magic... i don't like it).
If your object has required parameters in the constructor but you want to fetch it from the database the fetch_object lets you define arguments for the constructor so it can be constructed instead of throwing warnings/errors.
class Test {
private $title;
public function __construct($required) {
}
}
$mysql->fetch_object('Test'); //will trigger errors/warnings
$mysql->fetch_object('Test', array(null)); //won't
What you could do is simpel fetch an array and construct the object
function query($query, $class) {
$data = $query->fetch_assoc();
$instance = new $class($data);
return $instance;
}
The fetch_object() method will return an instance of stdClass. If you specify a class name, however, it'll return an instance of that class and populate attributes based on the data returned. You do NOT need to specify all of your columns in the class unless you want to change their visibility (i.e. protected or private). Otherwise they'll default to public.
Also, it's a good idea to have a static method within the class you want to instantiate to separate concerns neatly. See:
class Class_Name
{
public static function customQuery($query)
{
$return = array();
if ($result = $mysqli->query($query)) {
// not passing a class name to fetch_object()
// will return an instance of stdClass
while ($obj = $result->fetch_object('Class_Name')) {
// add the fetched object (as an instance of the
// 'Class_Name' class in this case) to the return array.
$return[] = $result;
}
return $return;
}
// return false if no results found
return false;
}
}
Call the static method thus and you'll get an array of 'Class_Name' objects:
$results = Class_Name::customQuery($query);
If you're only querying for 1 result the above would be like this:
class Class_Name
{
public static function customQuery($query)
{
$return = array();
if ($result = $mysqli->query($query)) {
return $result->fetch_object('Class_name');
}
// return false if no results found
return false;
}
}
With this you'll get a single 'Class_Name' object.
Note, if you're using a namespaced class apply the fully qualified namespaced class name.
There is not much use for this function.
Better make your object's constructor to accept an array with settings and use like this
while ($row = $result->fetch_assoc($res) {
$array[] = new Foo($row);
}
also note that fetch_assoc/fetch_object isn't always available with prepared statements.
You use fetch_object on a result. You do while ($obj = $result->fetch_query()) { ... }
function customQuery($query) {
if ($result = $mysqli->query($query)) {
while ($obj = $result->fetch_object()) {
# do stuff.
}
} else {
# raise some error, query did not make it.
}
}
If you specify param, this is a class that will be instantiated to handle the results.
# $result->fetch_object('MyClass');
MyClass {
private $id;
public function __construct($id/* fields */) {
$this->id = $id; # say `id` is the only field
}
}
Here is the proper way to use it:
<?php
$res = $mysqli->query($q);
while($params = $res->fetch_assoc()):
endwhile;
$res->free();
$res = $mysqli->query($q);
while ($obj = $res->fetch_object('ClassName', array($params)):
$obj_list[] = $obj;
endwhile;
?>
mysqli::fetch_object() doesn't send the fields names and values to the constructor, it just creates an object with the attributes without passing by the setters. If you want to pass by the constructor, you must first recover the result and give it to him in an array in parameters.
So what's happening if you want to pass by the constructor? At first, fetch_object() will create the attributes then it will overwrite them passing by the constructor.
Related
When using mysqli_fetch_object(), you can pass the name of an object as the second parameter. By doing so, mysqli creates the corresponding object and automatically sets the variables.
objectname {
// No defined properties
}
(...)
$object = mysqli_fetch_object($result, "objectname");
Under the assumption that $result contains data for "forname" and "surname", we would now have access to:
$object->forename;
$object->surname;
Is it possible to fetch the data into an associative array instead? Like shown here:
objectname {
public $data = array();
}
(...)
$object = mysqli_fetch_object($result, "objectname");
And then having:
$object->data["forename"];
$object->data["surname"];
If not: What is the code, MySQLi uses to populate the object with mysqli_fetch_object()? Considering that MySQLi is even able to change predefined private variables, it is a total mystery for me.
Unfortunately, you can't do it with mysqli. Usually, I would recommend PDO in such situations but even PDO doesn't have such capability.
There is a workaround though. You can create your own class with a constructor and a private property.
class MyClass
{
public function __construct(private array $data) {}
}
$stmt = $mysqli->prepare('SELECT id, name FROM users');
$stmt->execute();
$result = $stmt->get_result();
$data = [];
foreach ($result as $row) {
$data[] = new MyClass($row);
}
var_dump($data);
In the code above I am iterating over the result set and creating a new object passing the row each time as a constructor argument. You can control the visibility of the property using this approach.
P.S. If you are using PHP 7 then you can define the class this way:
class MyClass
{
private array $data;
public function __construct(array $data)
{
$this->data = $data;
}
}
I'm still new to OOP and this is probably a simple question, not sure if I'm overthinking this.
Let's say we have a simple class like the following that we can use to instantiate an object that can generate an array:
class gen_arr {
public $arr = array();
public function fill_arr() {
$this->arr["key"] = "value";
}
}
// instantiate object from gen_arr
$obj = new gen_arr();
Now if you wanted to get the value of the object's array's item, would you generate an array first and then echo the value like:
$arr = $obj->fill_arr();
echo $arr["key"];
Or would you access the object's property directly?
echo $obj->arr["key"]
In the actual code the property is private and there is a method that allows the viewing of the property array, the above is just to simplify the question.
Are there performance considerations and/or just best practices when it comes to this kind of case?
UPDATE:
It's still unclear from the answers if the best way is to generate an array from the property and access that array or just access the property directly (through the getter method)
Since you are filling the array with items only on fill_arr, those items wont be availabl until you call $arr = $obj->fill_arr();.
If you want to directly call the array, then you have to fill this array on the constructor function of this call like this:
class gen_arr {
public $arr = array();
function __construct() {
$this->arr["key"] = "value";
}
}
First off, the class you shared with us has a range of problems:
its sole instance property is public and can be modified by anyone
you have some temporal coupling, the method fill_arr() needs to be invoked before accessing the the value makes any sense
Encapsulation
Reduce the visibility of the instance property from public to private, so that the property can only be modified by the object itself, and provide an accessor instead:
class gen_arr
{
private $arr;
public function fill_arr()
{
$this->arr["key"] = "value";
}
public function arr()
{
return $this->arr;
}
}
Temporal Coupling
Remove the method fill_arr() and instead initialize the property $arr in one of the following options:
initialize field lazily when accessed the first time
initialize field in the constructor
initialize field with a default value
initialize field with a value injected via constructor
Initialize field lazily when accessed the first time
Initialize the field when it's accessed the first time:
class gen_arr
{
private $arr;
public function arr()
{
if (null === $this->arr) {
$this->arr = [
'key' => 'value',
];
}
return $this->arr;
}
}
Initialize field in the constructor
Assign a value during construction:
class gen_arr
{
private $arr;
public function __construct()
{
$this->arr = [
'key' => 'value',
];
}
public function arr()
{
return $this->arr;
}
}
Initialize field with a default value
Assign a value to the field directly, which works fine if you don't need to do any computation:
class gen_arr
{
private $arr = [
'key' => 'value',
];
public function arr()
{
return $this->arr;
}
}
Initialize field with a value injected via constructor
If the values are not hard-coded or otherwise calculated (as in the previous examples), and you need to be able to instantiate objects with different values, inject values via constructor:
class gen_arr
{
private $arr;
public function __construct(array $arr)
{
$this->arr = $arr;
}
public function arr()
{
return $this->arr;
}
}
Accessing and dereferencing values
This seems like this is your actual question, so the answer is - of course - It depends!.
Let's assume we have provided an accessor instead of accessing the otherwise public field directly:
Since PHP 5.4, the following is possible:
$object = new gen_arr();
echo $object->arr()['key'];
If you are still using an older version of PHP, you obviously can't do that and have to do something like this instead:
$object = new gen_arr();
$arr = $object->arr();
echo $arr['key'];
Largely, though, the answer to this question depends on the circumstances, and what you want to achieve. After all, readability is key for maintenance, so it might just make sense for you to introduce an explaining variable.
Note About your example, you could just use an ArrayObject instead:
$arr = new \ArrayObject([
'key' => 'value',
]);
echo $arr['key']);
For reference, see:
http://wiki.c2.com/?EncapsulationDefinition
http://blog.ploeh.dk/2011/05/24/DesignSmellTemporalCoupling/
http://php.net/manual/en/language.oop5.properties.php
http://wiki.c2.com/?ItDepends
http://php.net/manual/en/migration54.new-features.php
https://refactoring.com/catalog/extractVariable.html
http://wiki.c2.com/?IntroduceExplainingVariable
http://php.net/manual/en/class.arrayobject.php
For an example, see:
https://3v4l.org/qVVBM
First fill up the array
$gen_arr = new gen_arr();
$gen_arr->fill_arr();
then get the values with a getter method
$val = $gen_arr->getValue($key);
A getter method would be like this
public function getValue($key) {
return $this->arr[$key];
}
And certailny make the $arr property private
I am interested in how this works:
<?php
$Query = $mysqli->query("select * from table");
$Query->fetch_array(); // <== How to make $Query a class/method like this?
?>
How do you assign a method to a variable and then have that variable be able to call another method like the $mysqli and $Query example above?
One way to achieve what you are (I think) referring to, is by returning an object. It goes against the principle of dependency injection, but it's one way to do it.
class MyClassA
{
public function myFunction()
{
return new MyClassB();
}
}
class MyClassB
{
public function execute()
{
return true;
}
}
// Start use
$class = new MyClassA();
// Assign variable to function which returns object
$newObj = $class->myFunction();
// Will write "1" because now $newObj is MyClassB()
echo $newObj->execute();
Another way is to return $this from the first method. The usage of the above object would work identical in this instance, however you also allow another principle known as method chaining:
class MyClassA
{
public function myFunction()
{
return $this;
}
public function execute()
{
return true;
}
}
// Same as above works
$class = new MyClassA();
$sameObj = $class->myFunction();
echo $sameObj->execute();
// Allowing for Method Chain
$class = new MyClassA();
// Allowing for chaining
echo $class->myFunction()->execute();
You have to instantiate your class and call it's function with ->
$class = new MyClass(); // Instantiate class
$class->myFunction(); // Use it's function
PHP manual example of creating a class:
http://php.net/manual/en/language.oop5.basic.php
This thread didn't helped me.
If I use
$class_vars = get_class_vars(get_class($this));
foreach ($class_vars as $name => $value) {
echo "$name : $value\n";
}
I get
attrib1_name : attrib2_name : attrib3_name
There are no values. Also a private attribute is shown, which I don't want.
If I use
echo "<pre>";
print_r(get_object_vars($this));
echo "</pre>";
I get
Array
(
[atrrib1_name] => attrib1_value
[attrib2_name] => attrib2_value
)
Here again I have a private attribute and all sub attributes. But this time I have the values. How can I constrain this to one level?
Isn't there a possibility to show all public attributes with their values of an object?
You are seeing non-public properties because get_class_vars works according to current scope. Since you are using $this your code is inside the class, so the non-public properties are accessible from the current scope. The same goes for get_object_vars which is probably a better choice here.
In any case, a good solution would be to move the code that retrieves the property values out of the class.
If you do not want to create a free function for that (why? seriously, reconsider!), you can use a trick that involves an anonymous function:
$getter = function($obj) { return get_object_vars($obj); };
$class_vars = $getter($this);
See it in action.
Update: Since you are in PHP < 5.3.0, you can use this equivalent code:
$getter = create_function('$obj', 'return get_object_vars($obj);');
$class_vars = $getter($this);
You can do this easily with php Reflection api
Extending Mr.Coder's answer, here is a snippet to fetch the public attributes of the object (name and value) as an array
public function getPublicProperties()
{
$results = [];
$reflectionObject = (new ReflectionObject($this));
$properties = $reflectionObject->getProperties(ReflectionProperty::IS_PUBLIC);
foreach ($properties as $property) {
$results[$property->getName()] = $property->getValue($this);
}
return $results;
}
Use get_object_vars.
see: http://dk.php.net/manual/en/function.get-object-vars.php
I Fully recognize what you are trying to achieve so why not have something external like this to help out... (pasted from PHPFiddle)
<?php
final class utils {
public static function getProperties(& $what) {
return get_object_vars($what);
}
}
class ball {
var $name;
private $x, $y;
function __construct($name,$x,$y) {
}
function publicPropsToArray() {
return utils::getProperties($this);
}
function allPropsToArray() {
return get_object_vars($this);
}
}
$ball1 = new ball('henry',5,6);
//$ball2 = new ball('henry',3,4);
echo "<pre>";
print_r($ball1->publicPropsToArray());
echo "\r\n\r\n";
print_r($ball1->allPropsToArray());
echo "\r\n\r\n";
?>
This way I can both access all properties of the object or for something such as a database access layer or similarly for a function that send "safe" data to a view or another un-privileged model I can send just the public properties, but have the behaviour defined within the object.
Sure this leads to coupling with a utility class, but to be fair not all couplings are bad, some are nesecarry to achieve an end goal, dont get bogged down by these things
I am writing some PHP. I have several classes that do not declare any properties, public or otherwise. I have a custom mySQL class that fetches objects from mySQL and sets property values for the newly init'd PHP object like so...
while ($row = mysql_fetch_assoc($result))
{
foreach($row as $key => $value)
{
$this->{$key} = $value;
}
}
This seems to work fine as I can then call said properties anywhere I please ... $this->my_auto_property etc. I cannot find any PHP docs that describe this as a way to overload a class object's properties.
Is this okay? I want to make sure it's not some sort of backwards compatibility that will evaporate in future releases of PHP.
This is not overloading any property, it's just setting a property using variable variables. Dynamically creating new properties has always been a feature of PHP. Of course, nobody can guarantee that it won't be deprecated, but the way PHP favors weak typing I'd say that's unlikely.
An alternative would be to store the values in an array and create a magic __get accessor to read them, if you want to increase encapsulation and your control over accessibility.
Try something like this:
<?php
/**
* This class creates a dynamic shell to
* define and set any Setter or Getter
*
* Example:
*
* $property = new DynamicProperties();
* $property->setFax("123-123-1234"); // set[anything here first letter upper case]("value here")
* echo $property->getFax()."\n"; // get[anything here first letter upper case]()
*/
class DynamicProperties {
private $properties;
public function __call($name, $args) {
if (preg_match('!(get|set)(\w+)!', $name, $match)) {
$prop = $match[2];
if ($match[1] == 'get') {
if (count($args) != 0) {
throw new Exception("Method '$name' expected 0 arguments, got " . count($args)."\n");
}
return $this->properties[$prop];
} else {
if (count($args) != 1) {
throw new Exception("Method '$name' expected 1 argument, got " . count($args)."\n");
}
$this->properties[$prop] = $args[0];
}
} else {
throw new Exception("Unknown method $name");
}
}
}
?>