Passed
Push — main ( 30e095...f6f8c3 )
by Sammy
02:19 queued 11s
created

TightModel.php (1 issue)

Labels
Severity
1
<?php
2
3
namespace HexMakina\TightORM;
4
5
use HexMakina\Crudites\Interfaces\SelectInterface;
6
use HexMakina\TightORM\Interfaces\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->get_id();
16
    }
17
18
    public function immortal(): bool
19
    {
20
        return self::IMMORTAL_BY_DEFAULT;
21
    }
22
23
    public function extract(ModelInterface $extract_model, $ignore_nullable = false)
24
    {
25
        $extraction_class = get_class($extract_model);
26
27
        $extraction_table = $extraction_class::table();
28
        foreach ($extraction_table->columns() as $column_name => $column) {
29
            $probe_name = $extraction_class::table_alias() . '_' . $column_name;
30
31
            if (!is_null($probe_res = $this->get($probe_name))) {
32
                $extract_model->set($column_name, $probe_res);
33
            } elseif (!$column->is_nullable() && $ignore_nullable === false) {
34
                return null;
35
            }
36
        }
37
38
        return $extract_model;
39
    }
40
41
    public function copy()
42
    {
43
        $class = get_called_class();
44
        $clone = new $class();
45
46
        foreach ($class::table()->columns() as $column_name => $column) {
47
            if (!is_null($column->default())) {
48
                continue;
49
            }
50
            if ($column->is_auto_incremented()) {
51
                continue;
52
            }
53
54
            $clone->set($column_name, $this->get($column_name));
55
        }
56
57
        unset($clone->created_by);
58
        return $clone;
59
    }
60
61
    public function toggle($prop_name)
62
    {
63
        parent::toggle_boolean(static::table(), $prop_name, $this->get_id());
64
    }
65
66
67
    public function validate(): array
68
    {
69
        return []; // no errors
70
    }
71
72
    public function before_save(): array
73
    {
74
        return [];
75
    }
76
77
    public function after_save()
78
    {
79
        return true;
80
    }
81
82
    // return array of errors
83
    public function save($operator_id)
84
    {
85
        try {
86
            if (!empty($errors = $this->traitor('before_save'))) {
0 ignored issues
show
The method traitor() does not exist on HexMakina\TightORM\TightModel. ( Ignorable by Annotation )

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

86
            if (!empty($errors = $this->/** @scrutinizer ignore-call */ traitor('before_save'))) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
87
                return $errors;
88
            }
89
90
            if (!empty($errors = $this->before_save())) {
91
                return $errors;
92
            }
93
94
            if (!empty($errors = $this->validate())) { // Model level validation
95
                return $errors;
96
            }
97
98
            //1 tight model *always* match a single table row
99
            $table_row = $this->to_table_row($operator_id);
100
101
102
            if ($table_row->is_altered()) { // someting to save ?
103
                if (!empty($persistence_errors = $table_row->persist())) { // validate and persist
104
                    $errors = [];
105
                    foreach ($persistence_errors as $column_name => $err) {
106
                        $errors[sprintf('MODEL_%s_FIELD_%s', static::model_type(), $column_name)] = $err;
107
                    }
108
109
                    return $errors;
110
                }
111
112
                // reload row
113
                $refreshed_row = static::table()->restore($table_row->export());
114
115
                // update model
116
                $this->import($refreshed_row->export());
117
            }
118
119
            $this->traitor('after_save');
120
            $this->after_save();
121
        } catch (\Exception $e) {
122
            return [$e->getMessage()];
123
        }
124
125
        return [];
126
    }
127
128
    // returns false on failure or last executed delete query
129
    public function before_destroy(): bool
130
    {
131
        if ($this->is_new() || $this->immortal()) {
132
            return false;
133
        }
134
135
        $this->traitor(__FUNCTION__);
136
137
        return true;
138
    }
139
140
    public function after_destroy()
141
    {
142
        $this->traitor(__FUNCTION__);
143
    }
144
145
    public function destroy($operator_id): bool
146
    {
147
        if ($this->before_destroy() === false) {
148
            return false;
149
        }
150
151
        $table_row = static::table()->restore(get_object_vars($this));
152
153
        if ($table_row->wipe() === false) {
154
            return false;
155
        }
156
157
        $this->after_destroy();
158
159
        return true;
160
    }
161
162
    //------------------------------------------------------------  Data Retrieval
163
    public static function query_retrieve($filters = [], $options = []): SelectInterface
164
    {
165
        $class = get_called_class();
166
        $query = (new TightModelSelector(new $class()))->select($filters, $options);
167
        return $query;
168
    }
169
170
    //------------------------------------------------------------  Introspection & Data Validation
171
    /**
172
     * Cascade of table name guessing goes:
173
     * 1. Constant 'TABLE_ALIAS' defined in class
174
     * 2. lower-case class name
175
     *
176
     */
177
    public static function table_alias(): string
178
    {
179
        return defined(get_called_class() . '::TABLE_ALIAS') ? static::TABLE_ALIAS : static::model_type();
180
    }
181
182
    public static function class_short_name()
183
    {
184
        return (new \ReflectionClass(get_called_class()))->getShortName();
185
    }
186
187
    public static function model_type(): string
188
    {
189
        return strtolower(self::class_short_name());
190
    }
191
192
    public static function select_also()
193
    {
194
        return ['*'];
195
    }
196
}
197