Follow Relation¶
Nene\Kit\FollowRelation — directed user follow/unfollow relationships with mutual-follow detection.
Schema¶
CREATE TABLE follow_relations (
follower_id VARCHAR(255) NOT NULL,
followee_id VARCHAR(255) NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (follower_id, followee_id)
);
CREATE INDEX idx_follow_relations_followee ON follow_relations (followee_id);
The composite primary key (follower_id, followee_id) ensures uniqueness and serves as the main lookup index. The secondary index on followee_id speeds up followers() queries.
API¶
| Method | Description |
|---|---|
follow(string $followerId, string $followeeId): bool |
Follow a user. Returns true on success, false if already following or self-follow attempt. |
unfollow(string $followerId, string $followeeId): bool |
Unfollow a user. Returns true if removed, false if not following. |
isFollowing(string $followerId, string $followeeId): bool |
Check whether $followerId follows $followeeId. |
followers(string $userId): array |
List users following $userId. |
following(string $userId): array |
List users $userId follows. |
isMutual(string $userA, string $userB): bool |
Check whether both users follow each other. |
Usage¶
$fr = new FollowRelation($pdo);
// Follow
$fr->follow('user:1', 'user:2'); // true
$fr->follow('user:1', 'user:2'); // false — already following
$fr->follow('user:1', 'user:1'); // false — self-follow
// Check
$fr->isFollowing('user:1', 'user:2'); // true
$fr->isFollowing('user:2', 'user:1'); // false — directional
// Lists
$fr->followers('user:2'); // [['follower_id' => 'user:1', 'created_at' => '...']]
$fr->following('user:1'); // [['followee_id' => 'user:2', 'created_at' => '...']]
// Mutual follow
$fr->follow('user:2', 'user:1');
$fr->isMutual('user:1', 'user:2'); // true
// Unfollow
$fr->unfollow('user:1', 'user:2'); // true
$fr->unfollow('user:1', 'user:2'); // false — not following
Key design points¶
- Self-follow prevention:
follow($id, $id)returnsfalsewithout touching the DB. - Idempotent follow:
INSERT OR IGNORE/INSERT IGNORE— duplicate follow is a no-op returningfalse. - Directional: A→B does not imply B→A.
isFollowingreflects only the stated direction. isMutualsymmetry:isMutual(A, B) === isMutual(B, A).- No framework dependency: Constructor accepts
?PDO, falls back toPdoConnection::getInstance().