📜 ⬆️ ⬇️

Arrays or Objects? I want a collection in pkhp!

What to hide, I like objects, and do not like associative arrays. And when I select a certain data set from the database, I want to receive a set of objects and not an array of arrays. And not just a set of objects, but the set I need and exactly as I want it. Previously, there was a sample from the base in 3 stages:
1. get an array of data from the database
2. go through the result
3. at each iteration, create an object and insert it into another array
Well, actually return the data out. This is not something that strained strongly, but I felt that there should be a simpler and more convenient way. And I found it - Collections.

I like the collections very much. I always wanted to have an array of the same type of data, in particular objects. Especially in conjunction with the database.
The most banal situation - I want to get an array of User objects from the database:
Db_users :: getAll () method
1. read data from the database
2. go through the array of results and drive each line into the User object
3. save the resulting User object to another array that we give out
Somehow it happens:
static public function getAll() { $result = array(); $data = $this->db->fetchAll(); foreach( $data as $row ) { $user = new User( $row ); $result[] = $user; } return $result; } 

Here we have such a simple and standard method, which takes all users from the database and returns an array of entities.
And somehow it is displayed in the template:
 <html> <head> <title>Users list</title> </head> <body> <table> <th> <td>User ID</td> <td>User login</td> <td>User email</td> <td>User name</td> </th> <?php foreach( $users as $user ): ?> <tr> <td><?php echo $user->id; ?></td> <td><?php echo $user->login; ?></td> <td><?php echo $user->email; ?></td> <td><?php echo $user->name; ?></td> </tr> <?php endforeach; ?> </table> </body> </html> 

And so constantly. Everything seems to work well, but something is wrong. The first thing that catches your eye is two cycles. The first is when creating an array of entities, and the second is at output. Two cycles of (roughly speaking) the same array - why? First thought - PDO!
Yes, he has a magic method fetchObj. It will suit many, but I don’t like one of its features:
 class A { public function __construct() { echo "<br>construct"; } public function __set($key, $val) { echo "<br>setter"; } } $pdo = new PDO('mysql:dbname=home;host=127.0.0.1', 'root'); $sql = 'SELECT id FROM `table`'; $res = $pdo->query($sql); var_dump( $res->fetchObject('A') );  : setter setter constructobject(A)#3 (0) { } 

(Good people suggest PDO :: FETCH_PROPS_LATE, this solves the problem of setting properties before calling the constructor.)
If it doesn't matter to you, then you can stop at this, I was not satisfied that the constructor is called after setting the properties (I created a filter in a separate class in the designer, I don’t want to insert crutches with checks for empty variables), and all fields were set as properties so we go on.
There is a great built-in solution - ArrayIterator. A good kind of object, with getters and setters like an array and the possibility of busting in foreach. Just what you need.
 class Collection extends ArrayIterator { } 

Change the getAll method a bit:
 static public function getAll() { $result = array(); $data = $this->db->fetchAll(); $result = new Collection( $data ); return $result; } 

And the pattern:
 <?php foreach( $users as $user ): ?> <tr> <td><?php echo $user['id']; ?></td> <td><?php echo $user['login']; ?></td> <td><?php echo $user['email']; ?></td> <td><?php echo $user['name']; ?></td> </tr> <?php endforeach; ?> 

Already better, but what is this collection? We are just a trite copied array. So not very interesting. I want to receive an object, and a full one.
We look at the methods ArrayIterator :: offsetGet (access by key as in an array) and ArrayIterator :: current (access when browsing in foreach and receiving an element by the function current) these methods return the value by key and the current element by the internal pointer, respectively, redefine:
 public function offsetGet( $index ) { if( empty( $this->_cache[$index] ) ) { //    $this->_cache[$index] = new User( parent::offsetGet[$index] ); } return $this->_cache[$index]; } public function current( ) { $index = parent::key(); if( empty( $this->_cache[$index] ) ) { //    $this->_cache[$index] = new User( parent::current() ); } return $this->_cache[$index]; } 

As a result, the template has become like it wanted:
 <?php foreach( $users as $user ): ?> <tr> <td><?php echo $user->id; ?></td> <td><?php echo $user->login; ?></td> <td><?php echo $user->email; ?></td> <td><?php echo $user->name; ?></td> </tr> <?php endforeach; ?> 

Ok, everything seems to be ok, got rid of unnecessary cycles, replaced the array with an object
As a final touch, you can add the returned class dynamically.

')

Source: https://habr.com/ru/post/125665/


All Articles