FaZeBookSocialNetwork / www / src / FaZeBook / Data.php
Data.php
Raw
<?php

namespace FaZeBook;

use FaZeBook\Data\Activity;
use FaZeBook\Data\Message;
use FaZeBook\Data\Post;
use FaZeBook\Data\User;
use PDO;
use R;

class Data {
    private static $instance;

    public static function getInstance() {
        if(is_null(self::$instance)) {
            self::$instance = new Data(self::DB_FILE);
        }
        return self::$instance;
    }


    private static $testingInstance;

    public static function getTestingInstance() {
        if(is_null(self::$testingInstance)) {
            if(file_exists(self::DB_FILE_TEST)) { unlink(self::DB_FILE_TEST); }
            self::$testingInstance = new Data(self::DB_FILE_TEST);
        }
        return self::$testingInstance;
    }

    const DATA_DIR = "../data";
    const DB_FILE = self::DATA_DIR . "/db.sqlite";
    const DB_FILE_TEST = self::DATA_DIR . "/test.sqlite";
    const SQL_INIT_FILE = "../sql/init.sql";

    private function __construct(string $dbFile) {
        if(!is_dir(self::DATA_DIR)) {
            mkdir(self::DATA_DIR);
        }
        if(!file_exists($dbFile)) {
            // RedBean does not like uppercase chars in column names. Does not matter, SQL is case insensitive
            $init_sql = strtolower(file_get_contents(self::SQL_INIT_FILE));
            $pdo = new PDO('sqlite:' . $dbFile);
            $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $pdo->exec($init_sql);
            $pdo = null; // closes the connection
        }
        R::setup('sqlite:' . $dbFile);
        R::freeze(TRUE); // prevents RedBeanPHP from modifying the database schema
        R::setAutoResolve(TRUE);
    }

    /* ********** Creation / Deletion / Basic queries ********** */

    /* ----- User ----- */

    public function addUser(string $firstName, string $lastName, string $email, string $password): User {
        $dbo = R::dispense('user');
        $dbo->firstName = $firstName;
        $dbo->lastName = $lastName;
        $dbo->email = $email;
        $dbo->passwordHash = "";
        $dbo->lastSeen = time();
        R::store($dbo);
        $user = new User($dbo);
        $user->setPassword($password);

        $this->addActivity($firstName . " " . $lastName . " hat sich beim FaZeBook angemeldet!");

        return $user;
    }

    public function getUser(int $userId) {
        $dbo = R::load('user', $userId);
        return $dbo->id == 0 ? null : new User($dbo);
    }

    public function getRandomUsers($num){
        $dbos = R::findAll('user', 'ORDER BY RANDOM() LIMIT ?', [$num]);
        return array_map(function($dbo) { return new User($dbo); }, $dbos);
    }

    public function getUserByEmail(string $email) {
        $dbo = R::findOne('user', 'email = ?', [ $email ]);
        return $dbo == null ? null : new User($dbo);
    }

    public function getUserByKarma(int $karma){
        $dbo = R::findOne('user', 'karma = ?', [ $karma ]);
        return $dbo == null ? null : new User($dbo);
    }

    public function getUserAvatarLink(User $user): string {
        $avatarUrl = $user->getAvatarUrl();
        if(is_null($avatarUrl)) {
            return "https://upload.wikimedia.org/wikipedia/commons/thumb/6/62/Bundesarchiv_Bild_183-R68588,_Otto_von_Bismarck.jpg/220px-Bundesarchiv_Bild_183-R68588,_Otto_von_Bismarck.jpg";
        }
        return $avatarUrl;
    }

    public function listUsers(): array {
        $dbos = R::findAll('user');
        return array_map(function($dbo) { return new User($dbo); }, $dbos);
    }

    public function deleteUser(User $user) {
        R::exec('DELETE FROM Post WHERE Author_ID = :id OR Page_User_ID = :id',
            [':id' => $user->dbo->id]);
        R::exec('DELETE FROM Message WHERE Sender_ID = :id OR Recipient_ID = :id',
            [':id' => $user->dbo->id]);
        R::exec('DELETE FROM Friend WHERE Friend_A_ID = :id OR Friend_B_ID = :id',
            [":id" => $user->dbo->id]);
        R::exec('DELETE FROM Like WHERE Liker_ID = :id OR Liked_ID = :id',
            [":id" => $user->dbo->id]);
        R::trash($user->dbo);
    }

    /* ----- Post ----- */

    public function addPost(User $author, User $pageUser, bool $gossip, int $type, string $content): Post {
        $dbo = R::dispense('post');
        $dbo->author = $author->dbo;
        $dbo->page_user = $pageUser->dbo;
        $dbo->gossip = $gossip;
        $dbo->type = $type;
        $dbo->content = $content;
        $dbo->time = time();
        R::store($dbo);
        $post = new Post($dbo);

        if (!$gossip)
            $this->addActivity($author->getFullName() . " hat einen Beitrag auf dem Profil von " . $pageUser->getFullName() . " verfasst!");

        return $post;
    }

    private function listPosts(User $user, string $fieldName, bool $includeGossip): array {
        $condition = ' ' . $fieldName . ' = ?';
        if(!$includeGossip) {
            $condition .= ' AND Gossip = 0';
        }
        $condition .= ' ORDER BY Time DESC';
        $dbos = R::find('post', $condition, [ $user->dbo->id ]);
        return array_map(function($dbo) { return new Post($dbo); }, $dbos);
    }

    public function listPagePosts(User $user, bool $includeGossip): array {
        return $this->listPosts($user, 'Page_User_ID', $includeGossip);
    }

    public function listAuthoredPosts(User $author): array {
        return $this->listPosts($author, 'Author_ID', true);
    }

    public function deletePost(Post $post) {
        $this->addActivity("Auf dem Profil von " . $post->getPageUser()->getFullName() . " wurde ein Post von " . $post->getAuthor()->getFullName() . " gelöscht!");
        R::trash($post->dbo);
    }

    public function countPostsAuthor($userID) : int{
        return R::count('post', 'author_id = ?', [$userID]);
    }

    public function countPostsAuthorByType($userID, $type) : int{
        return R::count('post', 'author_id = :id AND type = :tt',
                            [":id" => $userID, ":tt" => $type]);
    }

    public function countPostsAuthorGossip($userID) : int{
        return R::count('post', 'author_id = ? AND gossip != 0', [$userID]);
    }

    public function countPostsReceived($userID) : int{
        return R::count('post', 'page_user_id = ?', [$userID]);
    }

    public function countPostsReceivedByType($userID, $type) : int{
        return R::count('post', 'page_user_id = :id AND type = :tt',
            [":id" => $userID, ":tt" => $type]);
    }

    public function countPostsReceivedGossip($userID) : int{
        return R::count('post', 'page_user_id = ? AND gossip != 0', [$userID]);
    }

    public function getRandomGossip(){
        $dbo = R::findOne('post', 'WHERE gossip != 0 ORDER BY RANDOM() LIMIT 1');
        return $dbo == null ? null : new Post($dbo);
    }

    /* ----- Message ----- */

    public function addMessage(User $sender, User $recipient, string $content): Message {
        $dbo = R::dispense('message');
        $dbo->sender = $sender->dbo;
        $dbo->recipient = $recipient->dbo;
        $dbo->time = time();
        $dbo->content = $content;
        $dbo->read = false;
        R::store($dbo);
        $message = new Message($dbo);
        return $message;
    }

    public function listMessagesBetween(User $userA, User $userB): array {
        $condition = ' (Sender_ID = :userA AND Recipient_ID = :userB)'
                    . ' OR (Sender_ID = :userB AND Recipient_ID = :userA)'
                    . ' ORDER BY Time ASC';
        $dbos = R::find('message', $condition, [ "userA" => $userA->dbo->id, "userB" => $userB->dbo->id ]);
        return array_map(function($dbo) { return new Message($dbo); }, $dbos);
    }

    public function countUnreadMessagesBetween(User $recipient, User $sender): int {
        $condition = ' (Sender_ID = :sender AND Recipient_ID = :recipient)'
            . ' AND (NOT read)';
        return R::count('message', $condition, [ "recipient" => $recipient->dbo->id, "sender" => $sender->dbo->id ]);
    }

    public function deleteMessage($message) {
        R::trash($message->dbo);
    }

    public function countMessagesAuthor($userID) : int{
        return R::count('message', 'sender_id = ?', [$userID]);
    }

    public function countMessagesRecipient($userID) : int{
        return R::count('message', 'recipient_id = ?', [$userID]);
    }

    /* ----- Friends ----- */

    public function setFriend(User $userA, User $userB) {
        if($this->isFriend($userA, $userB)) { return; }
        $dbo = R::dispense('friend');
        $dbo->friendA = $userA->dbo;
        $dbo->friendB = $userB->dbo;
        R::store($dbo);

        $this->addActivity($userA->getFullName() . " und " . $userB->getFullName() . " sind jetzt befreundet!");
    }

    public function isFriend(User $userA, User $userB): bool {
        if($userA->getId() === $userB->getId()) { return true; }
        return R::count('friend', ' (Friend_A_ID = :userA AND Friend_B_ID = :userB)'
            . ' OR (Friend_A_ID = :userB AND Friend_B_ID = :userA)', [
            "userA" => $userA->getId(),
            "userB" => $userB->getId()]) > 0;
    }

    public function listFriends(User $user): array {
        $condition = ' Id IN ('
            . ' SELECT Friend_A_ID FROM Friend WHERE Friend_B_ID = :user'
            . ' UNION'
            . ' SELECT Friend_B_ID FROM Friend WHERE Friend_A_ID = :user'
            . ')';
        $dbos = R::find('user', $condition, [ "user" => $user->dbo->id ]);
        return array_map(function($dbo) { return new User($dbo); }, $dbos);
    }

    public function deleteFriend(User $userA, User $userB) {
        R::exec('DELETE FROM Friend WHERE (Friend_A_ID = :userA AND Friend_B_ID = :userB)'
            . ' OR (Friend_A_ID = :userB AND Friend_B_ID = :userA)', [
            "userA" => $userA->getId(),
            "userB" => $userB->getId()]);

        $this->addActivity($userA->getFullName() . " und " . $userB->getFullName() . " sind jetzt keine Freunde mehr!");
    }

    public function countFriends($userID){
        return R::count('friend', 'friend_a_id = :id OR friend_b_id = :id', [':id' => $userID]);
    }

    /* ----- Likes ----- */
    // memo: $user->dbo->alias('liker')->aggr('ownLikeList', 'liked', 'user');
    //       returns a list of all people the user liked

    public function setLike(User $liker, User $liked, int $likeType) {
        $dbo = R::dispense('like');
        $dbo->liker = $liker->dbo;
        $dbo->liked = $liked->dbo;
        $dbo->type = $likeType;
        R::store($dbo);
    }

    public function getLike(User $liker, User $liked) {
        $res = R::getCell('SELECT Type FROM Like WHERE Liker_ID = ? AND Liked_ID = ?',
            [$liker->getId(), $liked->getId()]);
        return $res === null ? null : (int) $res;
    }

    public function deleteLike(User $liker, User $liked) {
        R::exec('DELETE FROM Like WHERE Liker_ID = ? AND Liked_ID = ?', [$liker->getId(), $liked->getId()]);
    }

    public function getMaxKarma() : int{
        return (int) R::getCell('SELECT MAX (karma) FROM user');
    }

    public function getMinKarma() : int{
        return (int) R::getCell('SELECT MIN (karma) FROM user');
    }

    public function countLikes($userID) : int{
        return R::count('like', 'liked_id = ? AND type = ' . LikeType::LIKE, [$userID]);
    }

    public function countDislikes($userID) : int{
        return R::count('like', 'liked_id = ? AND type = ' . LikeType::DISLIKE, [$userID]);
    }

    /* ----- Activity ----- */

    public function addActivity(string $event): Activity {
        $dbo = R::dispense('activity');
        $dbo->time = time();
        $dbo->event = $event;
        R::store($dbo);
        $activity = new Activity($dbo);
        return $activity;
    }

    public function listActivity(int $limit) {
        $dbos = R::find('activity', " ORDER BY TIME DESC LIMIT ?", [ $limit ]);
        return array_map(function($dbo) { return new Activity($dbo); }, $dbos);
    }

    /* ********** Actions that modify more than one database row ********** */

    public function quickScope(User $fragger, User $noob) {
        $fragger->dbo->kills++;
        $noob->dbo->deaths++;
        // TODO: Maybe log an event or something? idk
    }
}

?>