Model::filterByFields()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 1
dl 0
loc 11
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace alkemann\h2l\traits;
4
5
use alkemann\h2l\Connections;
6
use alkemann\h2l\exceptions;
7
use alkemann\h2l\exceptions\ConfigMissing;
8
use alkemann\h2l\interfaces\Source;
9
use Generator;
10
11
/**
12
 * Class Model
13
 *
14
 * Use this for prototyping only, use a real ORM for production stud
15
 *
16
 * Depends on \alkemann\h2l\Entity trait
17
 *
18
 * @property string $pk
19
 * @property string $connection
20
 * @property array $fields
21
 * @property string $table
22
 * @property array $data
23
 * @package alkemann\h2l
24
 */
25
trait Model
26
{
27
    /**
28
     * @throws ConfigMissing
29
     */
30
    public static function db(): Source
31
    {
32
        $name = isset(static::$connection) ? static::$connection : 'default';
33
        return Connections::get($name);
34
    }
35
36
    private static function pk(): string
37
    {
38
        return isset(static::$pk) ? static::$pk : 'id';
39
    }
40
41
    public function exists(): bool
42
    {
43
        // @TODO set a "read from db" property?
44
        $pk = static::pk();
45
        return isset($this->$pk) && $this->$pk;
46
    }
47
48
    private static function table(): string
49
    {
50
        if (!isset(static::$table)) {
51
            throw new ConfigMissing(
52
                get_called_class() . ' is missing static::$table',
53
                ConfigMissing::MISSING_TABLE
54
            );
55
        }
56
        return static::$table;
57
    }
58
59
    /**
60
     * @TODO throw exception instead of returning null of insert failed?
61
     * @param int|string $id
62
     * @param array<string, mixed> $conditions
63
     * @param array<string, mixed> $options
64
     * @return null|object
65
     */
66
    public static function get($id, array $conditions = [], array $options = []): ?object
67
    {
68
        if (empty($conditions) === false) {
69
            throw new \InvalidArgumentException("Conditions is not implemented on get");
70
        }
71
        $pk = static::pk();
72
        $conditions[$pk] = $id;
73
        $result = static::db()->one(static::table(), $conditions, $options);
74
        if ($result) {
75
            return new static($result);
76
        }
77
        return null;
78
    }
79
80
    /**
81
     * Find all records matching $conditions, returns a generator
82
     *
83
     * @param array<string, mixed> $conditions
84
     * @param array<string, mixed> $options
85
     * @return Generator
86
     * @throws ConfigMissing
87
     */
88
    public static function find(array $conditions = [], array $options = []): Generator
89
    {
90
        $conditions = self::filterByFields($conditions);
91
        $with = array_key_exists('with', $options) ? (array)$options['with'] : false;
92
        unset($options['with']);
93
        $result = static::db()->find(static::table(), $conditions, $options);
94
        $pk = static::pk();
95
        $gen = function() use ($result, $pk, $with) {
96
            foreach ($result as $row) {
97
                $model = new static($row);
98
                if ($with) {
99
                    $model->with(...$with);
100
                }
101
                $id = $row[$pk];
102
                yield $id => $model;
103
            }
104
        };
105
        return $gen();
106
    }
107
108
    /**
109
     * The implementation of this method will come from the Entity trait.
110
     *
111
     * @param string[] ...$relation_names list of names of relationships
112
     * @return object Instance of class that uses this trait
113
     */
114
    abstract public function with(string ...$relation_names): object;
115
    abstract public function reset(): void;
116
    /**
117
     * Update data of the model, return the (updated) dataset of model
118
     *
119
     * @param array|null $data
120
     * @return array
121
     */
122
    abstract public function data(array $data = null): array;
123
124
    /**
125
     * Find all records matching `$conditions`, returns an array with key being the pk value
126
     *
127
     * @param array<string, mixed> $conditions
128
     * @param array<string, mixed> $options
129
     * @return array
130
     * @throws ConfigMissing
131
     */
132
    public static function findAsArray(array $conditions = [], array $options = []): array
133
    {
134
        $generator = static::find($conditions, $options);
135
        return iterator_to_array($generator);
136
    }
137
138
    /**
139
     * @return array|null
140
     */
141
    public static function fields(): ?array
142
    {
143
        return isset(static::$fields) ? static::$fields : null;
144
    }
145
146
    /**
147
     * @param array<string, mixed> $data
148
     * @return array
149
     */
150
    private static function filterByFields(array $data): array
151
    {
152
        $fields = static::fields();
153
        if (empty($fields) === false) {
154
            $data = array_filter(
155
                $data,
156
                static fn(string $key) => in_array($key, $fields),
157
                ARRAY_FILTER_USE_KEY
158
            );
159
        }
160
        return $data;
161
    }
162
163
    /**
164
     * @param array<string, mixed> $data
165
     * @param array<string, mixed> $options
166
     * @return bool
167
     * @throws ConfigMissing
168
     */
169
    public function save(array $data = [], array $options = []): bool
170
    {
171
        $pk = static::pk();
172
        $db = static::db();
173
        $table = static::table();
174
175
        if ($this->exists()) {
176
            $id = $this->$pk;
177
            $data = self::filterByFields($data);
178
            unset($data[$pk]);
179
            $rows = $db->update($table, [$pk => $id], $data, $options);
180
            if (!$rows) {
181
                return false;
182
            }
183
        } else {
184
            $data += $this->data;
185
            $data = self::filterByFields($data);
186
            $id = $db->insert($table, $data, $options);
187
            if (!$id) {
188
                return false;
189
            }
190
        }
191
192
        $result = $db->one($table, [$pk => $id]);
193
        $this->reset();
194
        $this->data($result);
195
        return true;
196
    }
197
198
    /**
199
     * @param array<string, mixed> $options
200
     * @return bool
201
     * @throws exceptions\ConfigMissing
202
     */
203
    public function delete(array $options = []): bool
204
    {
205
        $pk = static::pk();
206
        return static::db()->delete(static::table(), [$pk => $this->$pk], $options);
207
    }
208
}
209