Junction::unlink()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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