Passed
Push — develop ( 575dfb...e45ca4 )
by Mathieu
01:42
created

DBCollection   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 307
Duplicated Lines 0 %

Test Coverage

Coverage 34.59%

Importance

Changes 0
Metric Value
wmc 46
eloc 125
dl 0
loc 307
ccs 46
cts 133
cp 0.3459
rs 8.72
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A addItem() 0 12 1
A getParentId() 0 3 1
A purgeItems() 0 5 1
A getTableName() 0 3 1
A lazyLoadFromSql() 0 18 4
A getLazyLoad() 0 3 1
A getItemsType() 0 3 1
A buildFromSql() 0 8 1
A save() 0 23 4
A connectDB() 0 6 3
A loadFromSql() 0 17 4
B loadForParentId() 0 44 9
A addItemLink() 0 6 1
A craftItem() 0 18 5
A setLazyLoad() 0 5 1
A loadAll() 0 18 3
A getParentIdField() 0 3 1
A setParentIdForAll() 0 11 3
A getDBConfig() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like DBCollection often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DBCollection, and based on these observations, apply Extract Interface, too.

1
<?php declare(strict_types=1);
2
3
namespace Suricate;
4
5
class DBCollection extends Collection
6
{
7
    /* @var string SQL table name */
8
    protected $tableName        = '';
9
    /* @var string Item type stored in collection */
10
    protected $itemsType        = '';
11
    /* @var string Database configuration identifier */
12
    protected $DBConfig         = '';
13
    /* @var string Name of parent identifier field */
14
    protected $parentIdField    = 'parent_id';
15
16
    protected $mapping          = [];
17
    protected $lazyLoad = false;
18
    protected $parentId;                       // Id of the parent
19
    protected $parentFilterName;                // Name of field used for filtering
20
    protected $parentFilterType;                // Value of filter
21
22
    protected $dbLink               = false;
23
    protected $itemOffset           = 0;
24
25
    /**
26
     * Get table name
27
     *
28
     * @return string
29
     */
30 2
    public function getTableName(): string
31
    {
32 2
        return $this->tableName;
33
    }
34
35 4
    public function getItemsType(): string
36
    {
37 4
        return $this->itemsType;
38
    }
39
40 1
    public function getDBConfig(): string
41
    {
42 1
        return $this->DBConfig;
43
    }
44
45 1
    public function getParentIdField(): string
46
    {
47 1
        return $this->parentIdField;
48
    }
49
50 1
    public function getParentId()
51
    {
52 1
        return $this->parentId;
53
    }
54
55
    /**
56
     * Set lazyload flag
57
     *
58
     * @param bool $lazyLoad
59
     * @return DBCollection
60
     */
61 1
    public function setLazyLoad($lazyLoad)
62
    {
63 1
        $this->lazyLoad = $lazyLoad;
64
65 1
        return $this;
66
    }
67
68
    /**
69
     * Get lazyload flag
70
     *
71
     * @return boolean
72
     */
73 1
    public function getLazyLoad(): bool
74
    {
75 1
        return $this->lazyLoad;
76
    }
77
78
    public function purgeItems()
79
    {
80
        $this->items        = [];
81
        $this->mapping      = [];
82
        $this->itemOffset   = 0;
83
    }
84
85
    /**
86
     * Load entire table into collection
87
     * @return Collection Loaded collection
88
     */
89 1
    public static function loadAll()
90
    {
91 1
        $calledClass    = get_called_class();
92 1
        $collection     = new $calledClass;
93
94 1
        $sqlParams      = [];
95
96 1
        $sql  = "SELECT *";
97 1
        $sql .= "   FROM `" . $collection->getTableName() . "`";
98
99 1
        if ($collection->parentFilterType !== '' && $collection->parentFilterType != null) {
100
            $sql .= "WHERE " . $collection->parentFilterName . "=:type";
101
            $sqlParams['type'] = $collection->parentFilterType;
102
        }
103
104 1
        $collection->loadFromSql($sql, $sqlParams);
105
106 1
        return $collection;
107
    }
108
109
    /**
110
     * Static wrapper for loadFromSql
111
     * @param  string     $sql       SQL Statement
112
     * @param  array      $sqlParams SQL Parameters
113
     * @return Collection Loaded collection
114
     */
115 1
    public static function buildFromSql($sql, $sqlParams = [])
116
    {
117 1
        $calledClass = get_called_class();
118 1
        $collection = new $calledClass;
119
120 1
        $collection->loadFromSql($sql, $sqlParams);
121
122 1
        return $collection;
123
    }
124
125
    /**
126
     * Load collection from SQL query
127
     *
128
     * @param string $sql       SQL query
129
     * @param array  $sqlParams associative array of SQL params
130
     * @return DBCollection
131
     */
132 3
    public function loadFromSql($sql, $sqlParams = [])
133
    {
134 3
        if (!in_array(Interfaces\IDBObject::class, class_implements($this->itemsType))) {
135
            throw new \BadMethodCallException('Item type does not implement IDBObject interface');
136
        }
137
138 3
        $this->connectDB();
139 3
        $results = $this->dbLink->query($sql, $sqlParams)->fetchAll();
140
141 3
        if ($results !== false) {
142 3
            foreach ($results as $currentResult) {
143 3
                $itemName = $this->getItemsType();
144 3
                $this->addItem($itemName::instanciate($currentResult));
145
            }
146
        }
147
148 3
        return $this;
149
    }
150
151
    public function addItemLink($linkId)
152
    {
153
         $this->items[$this->itemOffset] = $linkId;
154
         // add mapping between item->index and $position in items pool
155
         $this->mapping[$this->itemOffset] = $linkId;
156
         $this->itemOffset++;
157
    }
158
159
    public function lazyLoadFromSql($sql, $sqlParams = array())
160
    {
161
        $dbLink = Suricate::Database();
0 ignored issues
show
Bug introduced by
The method Database() does not exist on Suricate\Suricate. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

161
        /** @scrutinizer ignore-call */ 
162
        $dbLink = Suricate::Database();
Loading history...
162
        if ($this->DBConfig !== '') {
163
            $dbLink->setConfig($this->DBConfig);
164
        }
165
166
        $results = $dbLink
167
            ->query($sql, $sqlParams)
168
            ->fetchAll();
169
170
        if ($results !== false) {
171
            foreach ($results as $currentResult) {
172
                $this->addItemLink(current($currentResult));
173
            }
174
        }
175
176
        return $this;
177
    }
178
179
    /**
180
     * Load items linked to a parentId
181
     * @param mixed        $parentId       Parent id description
182
     * @param string       $parentIdField  Name of parent id referencing field
183
     * @param \Closure|null $validate       Callback use to validate add to items collection
184
     */
185
    public static function loadForParentId($parentId, $parentIdField = null, $validate = null)
186
    {
187
        $calledClass   = get_called_class();
188
        $collection     = new $calledClass;
189
190
        if ($parentId != '') {
191
            $sqlParams     = [];
192
            $dbHandler     = Suricate::Database(true);
193
194
            if ($collection->getDBConfig() !== '') {
195
                $dbHandler->setConfig($collection->getDBConfig());
196
            }
197
198
            $sql  = "SELECT *";
199
            $sql .= " FROM `" . $collection->getTableName() . "`";
200
            $sql .= " WHERE";
201
            if ($parentIdField !== null) {
202
                $sql .= "`" . $parentIdField . "`=:parent_id";
203
            } else {
204
                $sql .= "`" . $collection->getParentIdField() . "`=:parent_id";
205
            }
206
207
            if ($collection->parentFilterType !== null) {
208
                $sql .= "   AND " . $collection->parentFilterName . "=:parent_type";
209
                $sqlParams['parent_type'] = $collection->parentFilterType;
210
            }
211
212
            $sqlParams['parent_id'] = $parentId;
213
            $results = $dbHandler->query($sql, $sqlParams)->fetchAll();
214
215
            if ($results !== false) {
216
                foreach ($results as $currentResult) {
217
                    $itemName = $collection->getItemsType();
218
                    $item = $itemName::instanciate($currentResult);
219
                    if ($validate === null || $validate($item)) {
220
                        $collection->addItem($item);
221
                    }
222
                }
223
            }
224
225
            $collection->parentId = $parentId;
226
        }
227
228
        return $collection;
229
    }
230
231
    public function setParentIdForAll($parentId)
232
    {
233
        if ($this->parentIdField !== null) {
234
            $this->parentId = $parentId;
235
            foreach (array_keys($this->items) as $key) {
236
                $this->items[$key]->{$this->parentIdField} = $parentId;
237
            }
238
            return $this;
239
        }
240
241
        throw new \BadMethodCallException('Collection does not have a parentId field');
242
    }
243
244
    public function craftItem($itemData)
245
    {
246
        $itemName = $this->itemsType;
247
248
        foreach ($itemData as $data) {
249
            $newItem = new $itemName();
250
            $newItem->{$this->parentIdField} = $this->parentId;
251
            $hasData = false;
252
            foreach ($data as $field => $value) {
253
                $newItem->$field = $value;
254
                if ($value != '') {
255
                    $hasData = true;
256
                }
257
            }
258
259
            // Only add item if there's data inside
260
            if ($hasData) {
261
                $this->addItem($newItem);
262
            }
263
        }
264
    }
265
266
    public function save()
267
    {
268
        // 1st step : delete all records for current parentId
269
        $sql  = "DELETE FROM `" . $this->tableName . "`";
270
        if ($this->parentIdField !== '') {
271
            $sql .= " WHERE";
272
            $sql .= "   `" . $this->parentIdField . "`=:parent_id";
273
274
            $sqlParams = array('parent_id' => $this->parentId);
275
        } else {
276
            $sqlParams = array();
277
        }
278
279
        $dbLink = Suricate::Database();
280
        if ($this->DBConfig !== '') {
281
            $dbLink->setConfig($this->DBConfig);
282
        }
283
        
284
        $dbLink->query($sql, $sqlParams);
285
286
        // 2nd step : save all current items
287
        foreach ($this->items as $currentItem) {
288
            $currentItem->save(true); // Force insert
289
        }
290
    }
291
292 3
    public function addItem(Interfaces\IDBObject $item)
293
    {
294 3
        $key = $item->getTableIndex();
295
        // Add item to items pool
296 3
        $this->items[$this->itemOffset] = $item;
297
298
        // add mapping between item->index and $position in items pool
299 3
        $this->mapping[$this->itemOffset] = $item->$key;
300
301 3
        $this->itemOffset++;
302
303 3
        return $this;
304
    }
305
306 3
    protected function connectDB()
307
    {
308 3
        if (!$this->dbLink) {
309
            $this->dbLink = Suricate::Database();
310
            if ($this->getDBConfig() !== '') {
311
                $this->dbLink->setConfig($this->getDBConfig());
312
            }
313
        }
314 3
    }
315
}
316