Passed
Push — master ( b3ece8...6db175 )
by y
01:34
created

Junction::getClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
3
namespace Helix\DB;
4
5
use Helix\DB;
6
7
/**
8
 * Represents a junction table, derived from an annotated interface.
9
 *
10
 * Interface Annotations:
11
 *
12
 * - `@junction <TABLE>`
13
 * - `@foreign <COLUMN> <CLASS FQN>`
14
 *
15
 * @method static static factory(DB $db, string $interface)
16
 */
17
class Junction extends Table
18
{
19
20
    /**
21
     * `[column => class]`
22
     *
23
     * @var string[]
24
     */
25
    protected $classes = [];
26
27
    /**
28
     * @var Reflection
29
     */
30
    protected $ref;
31
32
    /**
33
     * @param DB $db
34
     * @param string $interface
35
     */
36
    public function __construct(DB $db, string $interface)
37
    {
38
        $this->ref = Reflection::factory($db, $interface);
39
        $this->classes = $this->ref->getForeignClasses();
40
        parent::__construct($db, $this->ref->getJunctionTable(), array_keys($this->classes));
41
    }
42
43
    /**
44
     * Returns a {@link Select} for entities referenced by a foreign key.
45
     *
46
     * The {@link Select} is literal and can be iterated directly.
47
     *
48
     * @param string $key The column referencing the class to collect.
49
     * @param array $match Keyed by junction column.
50
     * @return Select|EntityInterface[]
51
     */
52
    public function findAll(string $key, array $match = [])
53
    {
54
        $record = $this->getRecord($key);
55
        $select = $record->loadAll();
56
        $select->join($this, $this[$key]->isEqual($record['id']));
57
        foreach ($match as $a => $b) {
58
            $select->where($this->db->match($this[$a], $b));
59
        }
60
        return $select;
61
    }
62
63
    /**
64
     * @param string $column
65
     * @return string
66
     */
67
    final public function getClass(string $column): string
68
    {
69
        return $this->classes[$column];
70
    }
71
72
    /**
73
     * @return string[]
74
     */
75
    final public function getClasses()
76
    {
77
        return $this->classes;
78
    }
79
80
    /**
81
     * @param string $column
82
     * @return Record
83
     */
84
    public function getRecord(string $column)
85
    {
86
        return $this->db->getRecord($this->classes[$column]);
87
    }
88
89
    /**
90
     * @return Record[]
91
     */
92
    public function getRecords()
93
    {
94
        return array_map(fn($class) => $this->db->getRecord($class), $this->classes);
95
    }
96
97
    /**
98
     * `INSERT IGNORE` to link entities.
99
     *
100
     * @param int[]|EntityInterface[] $ids Keyed by column
101
     * @return int Rows affected
102
     */
103
    public function link(array $ids): int
104
    {
105
        $statement = $this->cache(__FUNCTION__, function () {
106
            $columns = implode(',', array_keys($this->columns));
107
            $slots = implode(',', $this->db->slots(array_keys($this->columns)));
108
            if ($this->db->isSQLite()) {
109
                $sql = "INSERT OR IGNORE INTO {$this} ({$columns}) VALUES ({$slots})";
110
            } else {
111
                $sql = "INSERT IGNORE INTO {$this} ({$columns}) VALUES ({$slots})";
112
            }
113
            return $this->db->prepare($sql);
114
        });
115
        $ids = array_map(fn($id) => $id instanceof EntityInterface ? $id->getId() : $id, $ids);
116
        $affected = $statement($ids)->rowCount();
117
        $statement->closeCursor();
118
        return $affected;
119
    }
120
121
    /**
122
     * Typed alias for {@link delete()}
123
     *
124
     * @param int[]|EntityInterface[] $ids Keyed by column
125
     * @return int Rows affected
126
     */
127
    public function unlink(array $ids): int
128
    {
129
        return $this->delete($ids);
130
    }
131
}
132