TableModel   B
last analyzed

Complexity

Total Complexity 48

Size/Duplication

Total Lines 267
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 1
Metric Value
wmc 48
eloc 96
c 5
b 0
f 1
dl 0
loc 267
rs 8.5599

18 Methods

Rating   Name   Duplication   Size   Complexity  
A id() 0 3 1
A pk() 0 8 3
A get() 0 7 2
A retrieve() 0 25 5
A set() 0 4 1
B actionnableParams() 0 31 8
A one() 0 18 3
A filter() 0 4 1
A any() 0 4 1
A count() 0 7 1
A table() 0 14 3
A exists() 0 6 2
A listing() 0 3 1
A get_many_by_AIPK() 0 9 3
A to_table_row() 0 22 5
A isNew() 0 4 1
A getId() 0 8 4
A import() 0 12 3

How to fix   Complexity   

Complex Class

Complex classes like TableModel 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 TableModel, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace HexMakina\TightORM;
4
5
use HexMakina\BlackBox\Database\SchemaInterface;
0 ignored issues
show
Bug introduced by
The type HexMakina\BlackBox\Database\SchemaInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
6
use HexMakina\Crudites\Crudites;
7
use HexMakina\Crudites\CruditesException;
8
use HexMakina\Crudites\Row;
0 ignored issues
show
Bug introduced by
The type HexMakina\Crudites\Row was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
9
use HexMakina\BlackBox\Database\SelectInterface;
10
11
abstract class TableModel extends Crudites
12
{
13
14
    private SchemaInterface $schema;
15
    private string $table;
16
17
    
18
    //check all primary keys are set (FIXME that doesn't work unles AIPK.. nice try)
19
    public function isNew(): bool
20
    {
21
        $match = static::table()->primaryKeysMatch(get_object_vars($this));
22
        return empty($match);
23
    }
24
25
    public function getId($mode = null)
26
    {
27
        $primary_key = $this->schema->autoIncrementedPrimaryKey(static::table());
28
        if (is_null($primary_key) && count($pks = static::table()->primaryKeys()) == 1) {
29
            $primary_key = current($pks);
30
        }
31
32
        return $mode === 'name' ? $primary_key->name() : $this->get($primary_key->name());
33
    }
34
35
    public function pk()
36
    {
37
        $primary_key = static::table()->autoIncrementedPrimaryKey();
38
        if (is_null($primary_key) && count($pks = static::table()->primaryKeys()) == 1) {
39
            $primary_key = current($pks);
40
        }
41
42
        return $this->get($primary_key->name());
43
    }
44
45
    public function id()
46
    {
47
        return $this->get('id');
48
    }
49
50
    public function get($prop_name)
51
    {
52
        if (property_exists($this, $prop_name)) {
53
            return $this->$prop_name;
54
        }
55
56
        return null;
57
    }
58
59
    public function set($prop_name, $value): void
60
    {
61
62
        $this->$prop_name = $value;
63
    }
64
65
    public function import($assoc_data): self
66
    {
67
        if (!is_array($assoc_data)) {
68
            throw new \Exception(__FUNCTION__ . '(assoc_data) parm is not an array');
69
        }
70
71
        // shove it all up in model, god will sort them out
72
        foreach ($assoc_data as $field => $value) {
73
            $this->set($field, $value);
74
        }
75
76
        return $this;
77
    }
78
79
80
81
    // relational mapping
82
    public static function table(): string
83
    {
84
        $called_class = new \ReflectionClass(get_called_class());
85
        $class_name = $called_class->getShortName();
86
87
        // try constants
88
        if (($table_name = $called_class->getConstant('TABLE_NAME')) !== false)
89
            return $table_name;
90
91
        if (defined($const_name = 'TABLE_' . strtoupper($class_name)))
92
            return constant($const_name);
93
        
94
        // fallback to convention
95
        return strtolower($class_name);
96
    }
97
98
    public function to_table_row($operator_id = null)
99
    {
100
        if (!is_null($operator_id) && $this->isNew() && is_null($this->get('created_by'))) {
101
            $this->set('created_by', $operator_id);
102
        }
103
104
        $model_data = get_object_vars($this);
105
106
        if($this->isNew()){
107
            // $table_row = new Row($this, $model_data);
108
            $table_row = new Row($this->schema, static::table(), $model_data);
109
        }
110
        else{
111
            $table_row = new Row($this->schema);
112
            $table_row->load($model_data);
113
        }
114
        // 1. Produce OR restore a row
115
116
        // 2. Apply alterations from form_model data
117
        $table_row->alter($model_data);
118
119
        return $table_row;
120
    }
121
122
    // success: return PK-indexed array of results (associative array or object)
123
    public static function retrieve(SelectInterface $select): array
124
    {
125
        $ret = [];
126
        $pk_name = implode('_', array_keys($select->table()->primaryKeys()));
127
128
        if (count($pks = $select->table()->primaryKeys()) > 1) {
129
            $concat_pk = sprintf('CONCAT(%s) as %s', implode(',', $pks), $pk_name);
130
            $select->selectAlso([$concat_pk]);
131
        }
132
133
        try {
134
            // vd($select);
135
            $select->run();
136
        } catch (CruditesException $e) {
137
            // dd($e);
138
            return [];
139
        }
140
141
        if ($select->isSuccess()) {
142
            foreach ($select->retObj(get_called_class()) as $rec) {
143
                $ret[$rec->get($pk_name)] = $rec;
144
            }
145
        }
146
147
        return $ret;
148
    }
149
150
151
    private static function actionnableParams($arg1, $arg2 = null): array
152
    {
153
        $unique_identifiers = null;
154
155
        $table = get_called_class()::table();
156
157
        // case 3
158
        if (is_array($arg1) && !empty($arg1)) {
159
            $unique_identifiers = $arg1;
160
        }
161
162
        // case 2
163
        else if (is_string($arg1) && is_scalar($arg2)) {
164
            $unique_identifiers = [$arg1 => $arg2];
165
        }
166
167
        // case 1
168
        else if (is_scalar($arg1) && count($table->primaryKeys()) === 1) {
169
            $pk = current($table->primaryKeys())->name();
170
            $unique_identifiers = [$pk => $arg1];
171
        } else
172
            throw new CruditesException('ARGUMENTS_ARE_NOT_ACTIONNABLE');
173
174
175
        // Find the unique identifier(s) in the database.
176
        $unique_identifiers = $table->matchUniqueness($unique_identifiers);
177
        if (empty($unique_identifiers)) {
178
            throw new CruditesException('UNIQUE_IDENTIFIER_NOT_FOUND');
179
        }
180
181
        return $unique_identifiers;
182
    }
183
184
185
    /**
186
     * Retrieve a single instance of the model by its unique identifier(s).
187
     * Throws CruditesException if the unique identifier yields no or multiple instances.
188
     *
189
     * @param mixed $arg1 The value of the primary key or an array of column-value pairs.
190
     * @param mixed|null $arg2 The value of the primary key if $arg1 is a string, otherwise null.
191
     * 
192
     * @return mixed The retrieved instance of the model.
193
     * 
194
     * @throws CruditesException If the arguments are not actionable, the unique identifier is not found, or multiple instances are found.
195
     * 
196
     * USAGE
197
     *  Case 1:  Class::one($primary_key_value)
198
     *  Case 2:  Class::one($unique_column, $value)
199
     *  Case 3:  Class::one([$unique_column => $value, $unique_column2 => $value2])
200
     * 
201
     */
202
    public static function one($arg1, $arg2 = null): self
203
    {
204
        $unique_identifiers = static::actionnableParams($arg1, $arg2);
205
        // vd($arg1, 'arg1');
206
        // vd($arg2, 'arg2');
207
        // vd($unique_identifiers, static::class);
208
209
        $records = static::any($unique_identifiers);
210
        // dd($records);
211
        switch (count($records)) {
212
            case 0:
213
                throw new CruditesException('NO_INSTANCE_MATCH_UNIQUE_IDENTIFIERS');
214
215
            case 1:
216
                return current($records);
217
218
            default:
219
                throw new CruditesException('MULTIPLE_INSTANCES_MATCH_UNIQUE_IDENTIFIERS');
220
        }
221
    }
222
223
    /**
224
     * Attempts to retrieve a single instance of the model by its unique identifier(s).
225
     * If no instance is found, returns null.
226
     */
227
228
    public static function exists($arg1, $arg2 = null): ?self
229
    {
230
        try {
231
            return self::one($arg1, $arg2);
232
        } catch (CruditesException $e) {
233
            return null;
234
        }
235
    }
236
237
    public static function any($field_exact_values = [], $options = [])
238
    {
239
        $select = static::filter($field_exact_values, $options);
240
        return static::retrieve($select);
241
    }
242
243
244
    public static function filter($filters = [], $options = []): SelectInterface
245
    {
246
        $query = (new TableModelSelector(get_called_class()))->select($filters, $options);
247
        return $query;
248
249
        // $query = static::query_retrieve($filters, $options);
250
        // return static::retrieve($query);
251
    }
252
253
    public static function count($filters = [], $options = []): int
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed. ( Ignorable by Annotation )

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

253
    public static function count($filters = [], /** @scrutinizer ignore-unused */ $options = []): int

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
254
    {
255
        $query = static::filter($filters, ['eager' => false]);
256
        $query->columns(['COUNT(*) as counter']);
257
        $res = static::retrieve($query);
258
        $res = array_pop($res);
259
        return (int)$res->counter;
260
    }
261
262
    public static function listing($filters = [], $options = []): array
263
    {
264
        return static::retrieve(static::filter($filters, $options)); // listing as arrays for templates
265
    }
266
267
268
269
    public static function get_many_by_AIPK($aipk_values): ?array
270
    {
271
        if (empty($aipk_values)) {
272
            return null;
273
        }
274
        if (is_null($AIPK = static::table()->autoIncrementedPrimaryKey())) {
275
            return null;
276
        }
277
        return static::retrieve(static::table()->select()->whereNumericIn($AIPK, $aipk_values));
278
    }
279
}
280