Passed
Push — main ( 793b19...197711 )
by Sammy
07:50 queued 14s
created

TightModel::urn()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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