Ctrl K
ComunidadGuías…

commands/DatabaseController.php

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
<?php

declare(strict_types=1);

namespace app\commands;

use app\models\db\{ISupporter, Motion, MotionSupporter, User};
use app\models\settings\AntragsgruenApp;
use yii\console\Controller;
use yii\console\ExitCode;

/**
 * @extends Controller<\yii\console\Application>
 *
 * Functions to create and destroy the database, and to fill it with initial data
 */
class DatabaseController extends Controller
{
    public const TEST_MODE_STD = 'std';
    public const TEST_MODE_YFJ = 'yfj';
    public const TEST_MODE_DBWV = 'dbwv';

    /**
     * Deletes the whole database. CAUTION!
     *
     * @throws \yii\db\Exception
     */
    public function actionDestroy(): void
    {
        if (!file_exists(__DIR__ . '/../config/DEBUG')) {
            $this->stderr('This action is only available in Debug-Mode' . "\n");
            return;
        }
        if ($this->confirm('Do you really want to DESTROY and reinitialize the database?')) {
            $deleteString = (string)file_get_contents(__DIR__ . '/../assets/db/delete.sql');
            $deleteString = str_replace('###TABLE_PREFIX###', AntragsgruenApp::getInstance()->tablePrefix, $deleteString);
            $command      = \Yii::$app->db->createCommand($deleteString);
            $command->execute();
        }
    }

    /**
     * Initializes an empty database with the current schema.
     *
     * Unlike database/create this is available outside debug mode, so that containerized
     * deployments can bootstrap a fresh database without the interactive web installer.
     * It refuses to touch a database that already has tables, which makes it safe to run
     * unconditionally on every container start.
     *
     * @throws \yii\db\Exception
     */
    public function actionInit(): int
    {
        $tablePrefix = AntragsgruenApp::getInstance()->tablePrefix;

        if (in_array($tablePrefix . 'site', \Yii::$app->db->schema->getTableNames(), true)) {
            $this->stdout('Database is already initialized, nothing to do.' . "\n");
            return ExitCode::OK;
        }

        foreach (['create.sql', 'data.sql'] as $file) {
            $sql = (string)file_get_contents(__DIR__ . '/../assets/db/' . $file);
            $sql = str_replace('###TABLE_PREFIX###', $tablePrefix, $sql);
            \Yii::$app->db->createCommand($sql)->execute();
        }

        // The web application caches table schemas for an hour, including the fact that a
        // table does not exist yet. A container that served any request before this ran -
        // a health check is enough - would otherwise keep reporting that the database is
        // not set up long after it is. Schema caching is only enabled for the web
        // application, so Schema::refresh() here would not clear it; drop the whole cache
        // instead, which is harmless on a database that was just created.
        $cache = \Yii::$app->has('cache') ? \Yii::$app->get('cache') : null;
        if ($cache instanceof \yii\caching\CacheInterface) {
            $cache->flush();
        }

        $this->stdout('Database initialized.' . "\n");

        return ExitCode::OK;
    }

    /**
     * Creates the tables
     *
     * @throws \yii\db\Exception
     */
    public function actionCreate(): void
    {
        if (!file_exists(__DIR__ . '/../config/DEBUG')) {
            $this->stderr('This action is only available in Debug-Mode' . "\n");
            return;
        }
        $createString = (string)file_get_contents(__DIR__ . '/../assets/db/create.sql');
        $createString = str_replace('###TABLE_PREFIX###', AntragsgruenApp::getInstance()->tablePrefix, $createString);
        $command      = \Yii::$app->db->createCommand($createString);
        $command->execute();

        $createString = (string)file_get_contents(__DIR__ . '/../assets/db/data.sql');
        $createString = str_replace('###TABLE_PREFIX###', AntragsgruenApp::getInstance()->tablePrefix, $createString);
        $command      = \Yii::$app->db->createCommand($createString);
        $command->execute();
    }

    /**
     * Insertes some test data into the tables. Do not use this on a production environment!
     *
     * @throws \yii\db\Exception
     */
    public function actionInsertTestData(string $testmode): void
    {
        if (!file_exists(__DIR__ . '/../config/DEBUG')) {
            $this->stderr('This action is only available in Debug-Mode' . "\n");
            return;
        }
        $testdata = match($testmode) {
            self::TEST_MODE_DBWV => (string)file_get_contents(__DIR__ . '/../tests/Support/Data/dbdata-dbwv.sql'),
            self::TEST_MODE_YFJ => (string)file_get_contents(__DIR__ . '/../tests/Support/Data/dbdata-yfj.sql'),
            default => (string)file_get_contents(__DIR__ . '/../tests/Support/Data/dbdata1.sql'),
        };
        $testdata = str_replace('###TABLE_PREFIX###', AntragsgruenApp::getInstance()->tablePrefix, $testdata);
        $command  = \Yii::$app->db->createCommand($testdata);
        $command->execute();
    }

    /**
     * Create tables and insert test data. For development only.
     *
     * @throws \yii\db\Exception
     */
    public function actionCreateTest(string $testmode = self::TEST_MODE_STD): void
    {
        if (!file_exists(__DIR__ . '/../config/DEBUG')) {
            $this->stderr('This action is only available in Debug-Mode' . "\n");
            return;
        }
        if ($this->confirm('Do you really want to DESTROY and reinitialize the database?')) {
            $deleteString = (string)file_get_contents(__DIR__ . '/../assets/db/delete.sql');
            $deleteString = str_replace('###TABLE_PREFIX###', AntragsgruenApp::getInstance()->tablePrefix, $deleteString);
            $command      = \Yii::$app->db->createCommand($deleteString);
            $command->execute();
            unset($command);

            $this->actionCreate();
            $this->actionInsertTestData($testmode);
        }
    }

    private function createOrGetUserAccount(string $email, string $password, string $givenName, string $familyName, string $organization): User
    {
        $user = User::findOne(['auth' => 'email:' . $email]);
        if ($user) {
            return $user;
        }

        $user = new User();
        $user->name = $givenName . ' ' . $familyName;
        $user->nameFamily = $familyName;
        $user->nameGiven = $givenName;
        $user->organization = $organization;
        $user->email = $email;
        $user->emailConfirmed = 1;
        $user->auth = 'email:' . $email;
        $user->dateCreation = date('Y-m-d H:i:s');
        $user->fixedData = User::FIXED_NAME | User::FIXED_ORGA;
        $user->status = User::STATUS_CONFIRMED;
        $user->pwdEnc = password_hash($password, PASSWORD_DEFAULT);
        $user->save();

        return $user;
    }

    private function supportMotion(User $user, Motion $motion, int $position): void
    {
        $support = new MotionSupporter();
        $support->motionId = $motion->id;
        $support->userId = $user->id;
        $support->personType = ISupporter::PERSON_NATURAL;
        $support->role = ISupporter::ROLE_SUPPORTER;
        $support->position = $position;
        $support->dateCreation = date('Y-m-d H:i:s');
        $support->save();
    }

    /**
     * Create thousands of supports for a motion
     */
    public function actionMassSupportMotion(int $motionId): void
    {
        if (!file_exists(__DIR__ . '/../config/DEBUG')) {
            $this->stderr('This action is only available in Debug-Mode' . "\n");
            return;
        }

        /** @var Motion|null $motion */
        $motion = Motion::findOne($motionId);
        if (!$motion) {
            $this->stderr('Motion not found' . "\n");
        }

        for ($i = 0; $i < 10000; $i++) {
            $user = $this->createOrGetUserAccount('test-' . $i . '@example.org', 'Test', 'Test', (string)$i, 'Orga');
            $this->supportMotion($user, $motion, $i);
        }
    }
}