TightModel   A
last analyzed

Complexity

Total Complexity 32

Size/Duplication

Total Lines 179
Duplicated Lines 0 %

Importance

Changes 6
Bugs 1 Features 1
Metric Value
wmc 32
eloc 61
c 6
b 1
f 1
dl 0
loc 179
rs 9.84

17 Methods

Rating   Name   Duplication   Size   Complexity  
A destroy() 0 14 3
A selectAlso() 0 3 1
A class_short_name() 0 3 1
A before_destroy() 0 9 3
A after_destroy() 0 3 1
A model_type() 0 7 2
A tableAlias() 0 7 2
B save() 0 37 7
A validate() 0 3 1
A before_save() 0 3 1
A after_save() 0 3 1
A nid() 0 3 1
A __toString() 0 3 1
A filter() 0 5 1
A immortal() 0 3 1
A urn() 0 3 1
A copy() 0 19 4
1
<?php
2
namespace HexMakina\TightORM;
3
4
use HexMakina\BlackBox\Database\SelectInterface;
5
use HexMakina\BlackBox\ORM\ModelInterface;
6
use HexMakina\Traitor\Traitor;
7
8
abstract class TightModel extends TableModel implements ModelInterface
9
{
10
    use Traitor;
11
    
12
    public function __toString()
13
    {
14
        return static::class_short_name() . ' #' . $this->id();
15
    }
16
17
    public function nid(): string
18
    {
19
        return static::class_short_name();
20
    }
21
22
    public function urn(): string
23
    {
24
        return $this->nid() . ':' . $this->id();
25
    }
26
    
27
    public function immortal(): bool
28
    {
29
        return self::IMMORTAL_BY_DEFAULT;
30
    }
31
32
    // creates new instance, copy data from $this
33
    // does not copy fields with default and AI field
34
    public function copy()
35
    {
36
        $class = get_called_class();
37
        $clone = new $class();
38
39
        foreach ($class::table()->columns() as $column_name => $column) {
40
            if (!is_null($column->default())) {
41
                continue;
42
            }
43
            if ($column->isAutoIncremented()) {
44
                continue;
45
            }
46
47
            $clone->set($column_name, $this->get($column_name));
48
        }
49
50
        // TODO: assuming created_by, must be a function that returns the creation tracking field, make an interface for heaven's sake
51
        unset($clone->created_by);
52
        return $clone;
53
    }
54
55
    public function validate(): array
56
    {
57
        return []; // no errors
58
    }
59
60
    public function before_save(): array
61
    {
62
        return [];
63
    }
64
65
    public function after_save()
66
    {
67
        return true;
68
    }
69
70
    // return array of errors on failure
71
    // updates the model on success
72
    public function save($operator_id)
73
    {
74
        try {
75
            // errors detection befire saving
76
            $errors = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $errors is dead and can be removed.
Loading history...
77
            if (empty($errors = $this->traitor('before_save'))) {
78
                if (empty($errors = $this->before_save())) {
79
                    $errors = $this->validate();
80
                }
81
            }
82
            if (!empty($errors)) {
83
                return $errors;
84
            }
85
86
            
87
            // a tight model *always* match a single table row
88
            $table_row = $this->to_table_row($operator_id);
89
            
90
            if ($table_row->isAltered()) { // someting to save ?
91
                
92
                $errors = $table_row->persist();
93
94
                if (!empty($errors)) {
95
                    return $errors;
96
                }
97
                
98
                $refreshed_row = static::table()->restore($table_row->export());
99
                $this->import($refreshed_row->export());
100
            }
101
102
            $this->traitor('after_save');
103
            $this->after_save();
104
        } catch (\Exception $e) {
105
            return [$e->getMessage()];
106
        }
107
108
        return [];
109
    }
110
111
    // returns false on failure or last executed delete query
112
    public function before_destroy(): bool
113
    {
114
        if ($this->isNew() || $this->immortal()) {
115
            return false;
116
        }
117
118
        $this->traitor(__FUNCTION__);
119
120
        return true;
121
    }
122
123
    public function after_destroy()
124
    {
125
        $this->traitor(__FUNCTION__);
126
    }
127
128
    public function destroy($operator_id): bool
129
    {
130
        if ($this->before_destroy() === false) {
131
            return false;
132
        }
133
        $table_row = static::table()->restore(get_object_vars($this));
134
135
        if ($table_row->wipe() === false) {
136
            return false;
137
        }
138
139
        $this->after_destroy();
140
141
        return true;
142
    }
143
144
    //------------------------------------------------------------  Data Retrieval
145
    public static function filter($filters = [], $options = []): SelectInterface
146
    {
147
        $class = static::class;
148
        $query = (new TightModelSelector(new $class()))->select($filters, $options);
149
        return $query;
150
    }
151
152
    //------------------------------------------------------------  Introspection & Data Validation
153
    /**
154
     * Cascade of table name guessing goes:
155
     * 1. Constant 'TABLE_ALIAS' defined in class
156
     * 2. lower-case class name
157
     *
158
     */
159
    public static function tableAlias(): string
160
    {
161
        if (defined(get_called_class() . '::TABLE_ALIAS')) {
162
            return get_called_class()::TABLE_ALIAS;
163
        }
164
165
        return static::model_type();
166
    }
167
168
    public static function model_type(): string
169
    {
170
        if (defined(get_called_class() . '::MODEL_TYPE')) {
171
            return get_called_class()::MODEL_TYPE;
172
        }
173
174
        return strtolower(self::class_short_name());
175
    }
176
177
    
178
    public static function class_short_name(): string
179
    {
180
        return (new \ReflectionClass(get_called_class()))->getShortName();
181
    }
182
183
184
    public static function selectAlso()
185
    {
186
        return ['*'];
187
    }
188
}
189