Completed
Push — master ( fc2d7d...add27a )
by Rasmus
02:14
created

Result::all()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
namespace mindplay\sql\framework;
4
5
use Iterator;
6
use IteratorAggregate;
7
use RuntimeException;
8
9
/**
10
 * This class represents the result of fetching a `PreparedStatement`, e.g. the results of
11
 * a `SELECT` SQL query, and with Mappers being applied on-the-fly, in batches.
12
 *
13
 * It implements `IteratorAggregate`, allowing you to execute the query and iterate
14
 * over the result set with a `foreach` statement.
15
 */
16
class Result implements IteratorAggregate
17
{
18
    /**
19
     * @var PreparedStatement
20
     */
21
    private $statement;
22
23
    /**
24
     * @var int
25
     */
26
    private $batch_size;
27
28
    /**
29
     * @var Mapper[] list of Mappers to apply when fetching results
30
     */
31
    private $mappers;
32
33
    /**
34
     * @param PreparedStatement $statement  prepared statement
35
     * @param int               $batch_size batch-size (when fetching large result sets)
36
     * @param Mapper[]          $mappers    list of Mappers to apply while fetching results
37
     */
38 1
    public function __construct(PreparedStatement $statement, $batch_size, array $mappers)
39
    {
40 1
        $this->statement = $statement;
41 1
        $this->batch_size = $batch_size;
42 1
        $this->mappers = $mappers;
43 1
    }
44
45
    /**
46
     * @return mixed|null first record of the record-set (or NULL, if the record-set is empty)
47
     */
48 1
    public function firstRow()
49
    {
50 1
        foreach ($this->createIterator(1) as $record) {
51 1
            return $record; // break from loop immediately after fetching the first record
52
        }
53
54
        return null;
55
    }
56
57
    /**
58
     * @return mixed|null first column value of the first record of the record-set (or NULL, if the record-set is empty)
59
     */
60 1
    public function firstCol()
61
    {
62 1
        foreach ($this->createIterator(1) as $record) {
63 1
            $keys = array_keys($record);
64
65 1
            return $record[$keys[0]]; // break from loop immediately after fetching the first record
66
        }
67
68
        return null;
69
    }
70
71
    /**
72
     * @return array all the records of the record-set
73
     */
74 1
    public function all()
75
    {
76 1
        return iterator_to_array($this->getIterator());
77
    }
78
    
79
    /**
80
     * Execute this Statement and return a Generator, so you can iterate over the results.
81
     *
82
     * This method implements `IteratorAggregate`, permitting you to iterate directly over
83
     * the resulting records (or objects) without explicitly having to call this method.
84
     *
85
     * @return Iterator
86
     */
87 1
    public function getIterator()
88
    {
89 1
        return $this->createIterator($this->batch_size);
90
    }
91
92
    /**
93
     * Create an Iterator with a given batch-size.
94
     *
95
     * @param int $batch_size batch-size when processing the result set
96
     *
97
     * @return Iterator
98
     */
99 1
    private function createIterator($batch_size)
100
    {
101 1
        $fetching = true;
102
103
        do {
104
            // fetch a batch of records:
105
106 1
            $batch = [];
107
108
            do {
109 1
                $record = $this->statement->fetch();
110
111 1
                if ($record) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $record of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
112 1
                    $batch[] = $record;
113
                } else {
114 1
                    if (count($batch) === 0) {
115 1
                        return; // last batch of records fetched
116
                    }
117
118 1
                    $fetching = false; // last record of batch fetched
119
                }
120 1
            } while ($fetching && (count($batch) < $batch_size));
121
122
            // apply Mappers to current batch of records:
123
124 1
            $num_records = count($batch);
125
126 1
            foreach ($this->mappers as $index => $mapper) {
127 1
                $batch = $mapper->map($batch);
128
129 1
                if (count($batch) !== $num_records) {
130
                    $count = count($batch);
131
132 1
                    throw new RuntimeException("Mapper #{$index} returned {$count} records, expected: {$num_records}");
133
                }
134
            }
135
136
            // return each record from the current batch:
137
138 1
            foreach ($batch as $record) {
139 1
                yield $record;
140
            }
141 1
        } while ($fetching);
142 1
    }
143
}
144