浏览代码

Базовая реализация расписания

Вадим Королёв 1 年之前
父节点
当前提交
14924046ea

+ 1 - 2
composer.json

@@ -17,8 +17,7 @@
     },
     "autoload": {
         "psr-4": {
-            "BotKit\\": "src/",
-	    "BotKit\\Tools\\": "tools/"
+            "BotKit\\": "src/"
         }
     }
 }

+ 4 - 0
index.php

@@ -17,6 +17,10 @@ use BotKit\Enums\State;
 use BotKit\Enums\CallbackType;
 use BotKit\Keyboards\SelectGroupNameKeyboard;
 
+ini_set('display_errors', '1');
+ini_set('display_startup_errors', '1');
+error_reporting(E_ALL);
+
 $bot = new Bot();
 $drv = new TestDriver();
 $bot->loadDriver($drv);

+ 24 - 1
src/Common/Commands.php

@@ -138,7 +138,30 @@ class Commands {
                 // 3. Составить расписание
                 $data = $em->getRepository(Entities\Pair::class)
                 ->getPairsOfScheduleForGroup($schedule);
-                $driver->sendMessage($u, Message::create($data->getSql()));
+
+                $text = "";
+                foreach ($data as $row) {
+                    $time = $row->getTime();
+                    $name = $row->getPairName()->getName();
+                    $details = $row->getConductionDetails();
+
+                    // Составление текста о преподавателе и месте проведения
+                    $details_texts = [];
+                    foreach ($details as $detail) {
+                        $initials = $detail->getEmployee()->getNameWithInitials();
+                        $place = $detail->getPlace()->getName();
+
+                        $details_text[] = $initials.' '.$place;
+                    }
+                    $details_text = implode(' / ', $details_text);
+
+                    $text .= $time->format('H:i');
+                    $text .= $name;
+                    $text .= ":".$details_text;
+                    $text .= "\n";
+                }
+
+                $driver->sendMessage($u, Message::create($text));
 
                 break;
             default:

+ 5 - 0
src/Entities/Employee.php

@@ -36,4 +36,9 @@ class Employee {
     public function setPatronymic(string $patronymic) : void {
         $this->patronymic = $patronymic;
     }
+
+    // Возвращает имя формата "Королёв В. С."
+    public function getNameWithInitials() : string {
+        return $this->surname." ".mb_substr($this->name, 0, 1).". ".mb_substr($this->patronymic, 0, 1).".";
+    }
 }

+ 21 - 8
src/Entities/Pair.php

@@ -4,6 +4,8 @@ namespace BotKit\Entities;
 
 use BotKit\Enums\State;
 use Doctrine\ORM\Mapping as ORM;
+use Doctrine\Common\Collections\ArrayCollection;
+use Doctrine\Common\Collections\Collection;
 
 #[ORM\Entity(repositoryClass: "PairRepo")]
 #[ORM\Table(name: 'pair')]
@@ -27,13 +29,24 @@ class Pair {
     #[ORM\JoinColumn(nullable: false)]
     private PairName $pair_name;
 
-    // Кто проводит пару
-    #[ORM\ManyToOne(Employee::class)]
-    #[ORM\JoinColumn(nullable: false)]
-    private Employee $teacher;
+    // Детали проведения
+    #[ORM\OneToMany(targetEntity: PairConductionDetail::class, mappedBy: 'pair')]
+    private Collection $conduction_details;
 
-    // Место проведения
-    #[ORM\ManyToOne(PairPlace::class)]
-    #[ORM\JoinColumn(nullable: false)]
-    private PairPlace $place;
+    public function __construct() {
+        $this->conduction_details = new ArrayCollection();
+    }
+
+    public function getPairName() : PairName {
+        return $this->pair_name;
+    }
+
+    public function getConductionDetails() {
+        return $this->conduction_details;
+    }
+
+    public function getTime() : \Datetime {
+        return $this->time;
+    }
+    
 }

+ 43 - 0
src/Entities/PairConductionDetail.php

@@ -0,0 +1,43 @@
+<?php
+// Связная сущность - хранит в себе информацию о проведении пары.
+// Необходимо в случаях когда одна пара проводится сразу в нескольких местами
+// несколькими преподавателями.
+// Например: английский язык может быть одновременно в двух подгруппах
+// сразу, в таком случае для этой пары два преподавателя (так же как и два места)
+
+namespace BotKit\Entities;
+
+use BotKit\Enums\State;
+use Doctrine\ORM\Mapping as ORM;
+
+#[ORM\Entity]
+#[ORM\Table(name: 'pair_conduction_detail')]
+class PairConductionDetail {
+    #[ORM\Id]
+    #[ORM\Column(type: 'integer')]
+    #[ORM\GeneratedValue]
+    private int|null $id = null;
+
+    // Для какой пары
+    #[ORM\ManyToOne(Pair::class, inversedBy: 'pair')]
+    #[ORM\JoinColumn(nullable: false)]
+    private Pair $pair;
+
+    // Какой преподаватель
+    #[ORM\ManyToOne(Employee::class)]
+    #[ORM\JoinColumn(nullable: false)]
+    private Employee $employee;
+
+    // В каком месте
+    #[ORM\ManyToOne(Place::class)]
+    #[ORM\JoinColumn(nullable: false)]
+    private Place $place;
+
+    public function getEmployee() : Employee {
+        return $this->employee;
+    }
+
+    public function getPlace() : Place {
+        return $this->place;
+    }
+}

+ 4 - 0
src/Entities/PairName.php

@@ -16,4 +16,8 @@ class PairName {
     // Название пары
     #[ORM\Column(type: 'string', length: 255)]
     private string $name;
+
+    public function getName() : string {
+        return $this->name;
+    }
 }

+ 12 - 11
src/Entities/PairRepo.php

@@ -8,17 +8,18 @@ class PairRepo extends EntityRepository {
 
     // Возвращает данные для отправки расписания группы
     public function getPairsOfScheduleForGroup(Schedule $schedule) {
-        $qb = $this->getEntityManager()->createQueryBuilder();
-
-        $pairs = $qb
-        ->select('pair', 'pair_name', 'teacher', 'place')
-        ->from(Pair::class, 'pair')
-        ->leftJoin('pair.pair_name', 'pair_name')
-        ->leftJoin('pair.teacher', 'teacher')
-        ->leftJoin('pair.place', 'place')
-        ->andWhere('pair.schedule = :schedule')
-        ->setParameter('schedule', $schedule)
-        ->getQuery();
+        $em = $this->getEntityManager();
+
+        $query = $em->createQuery(
+        'SELECT p, pn, cd FROM '.Pair::class.' p '.
+        'JOIN p.pair_name pn '.
+        'JOIN p.conduction_details cd '.
+        'WHERE p.schedule=:schedule '
+        );
+
+        $query->setParameters(['schedule'=>$schedule]);
+
+        $pairs = $query->getResult();
 
         return $pairs;
     }

+ 8 - 2
src/Entities/PairPlace.php → src/Entities/Place.php

@@ -1,4 +1,6 @@
 <?php
+// Место. Кабинет в техникуме, актовый зал -- что угодно, где может проводиться
+// пара.
 
 namespace BotKit\Entities;
 
@@ -6,8 +8,8 @@ use BotKit\Enums\State;
 use Doctrine\ORM\Mapping as ORM;
 
 #[ORM\Entity]
-#[ORM\Table(name: 'pair_place')]
-class PairPlace {
+#[ORM\Table(name: 'place')]
+class Place {
     #[ORM\Id]
     #[ORM\Column(type: 'integer')]
     #[ORM\GeneratedValue]
@@ -16,4 +18,8 @@ class PairPlace {
     // Имя места проведения пары
     #[ORM\Column(type: 'string', length: 255)]
     private string $name;
+
+    public function getName() : string {
+        return $this->name;
+    }
 }

+ 1 - 0
syntaxcheck.sh

@@ -0,0 +1 @@
+find ./src -name \*.php -exec php -l {} \; | grep -v "No syntax errors"

+ 0 - 135
tools/ModelCodeGenerator.php

@@ -1,135 +0,0 @@
-<?php
-
-namespace BotKit\Tools;
-
-// Класс генерации исходных файлов моделей
-
-use BotKit\Common\Database;
-
-class ModelCodeGenerator {
-
-    // Шаблон для класса
-    private static string $class_template =
-'<?php
-
-namespace BotKit\Models;
-
-class %sModel extends Model {
-    use %s;
-    protected static $allowed_columns = ["*"];
-}';
-
-    // Шаблон для трейта
-    private static string $trait_template =
-'<?php
-// Внимание! При повторном выполнении скрипта генерации моделей этот файл будет ПЕРЕЗАПИСАН
-// Прописывайте бизнес-логику в классах, а не в трейтах
-
-namespace BotKit\Models;
-
-use BotKit\Common\Database;
-
-trait %s {
-    protected static string $table_name = "%s";
-
-    // Пояснения колонок:
-%s
-    public static function create(%s) {
-        $db = Database::getConnection();
-        $statement = $db->prepare(
-            "INSERT INTO %s (%s) VALUES (%s)"
-        );
-%s
-        $statement->execute();
-        return $db->query("SELECT ".static::allowedColumnsSQL()." FROM %s WHERE id=".$db->lastInsertId())->fetch();
-    }
-
-    public static function updateObject($object) {
-        $db = Database::getConnection();
-        $statement = $db->prepare(
-            "UPDATE %s SET %s WHERE id=:id"
-        );
-        $statement->bindValue(":id", $object["id"]);
-%s
-        $statement->execute();
-    }
-}';
-
-    // Запускает процесс генерации
-    public static function run(string $user, string $password, string $db_name) : void {
-        // Получение информации о таблицах и колонках
-        Database::init(
-            'localhost',
-            'information_schema',
-            $user,
-            $password
-        );
-        $db = Database::getConnection();
-
-        // Запрос на получение всех таблиц в БД
-        $all_tables = $db->prepare(
-            "SELECT `TABLE_NAME` FROM `TABLES`
-            WHERE `TABLE_SCHEMA`=:table_schema"
-        );
-
-        // Запрос на получение всех колонок таблицы
-        $all_columns = $db->prepare(
-            "SELECT `COLUMN_NAME`, `COLUMN_COMMENT`, `COLUMN_KEY`, `IS_NULLABLE`
-            FROM `COLUMNS`
-            WHERE `TABLE_SCHEMA`=:table_schema AND `TABLE_NAME`=:table_name
-            ORDER BY `ORDINAL_POSITION`"
-        );
-
-        // Привязка данных
-        $all_tables->bindValue(':table_schema', $db_name);
-        $all_columns->bindValue(':table_schema', $db_name);
-        $all_columns->bindParam(':table_name', $table_name);
-
-        // Сбор таблиц
-        $all_tables->execute();
-        $tables = [];
-        while (($row_table = $all_tables->fetch()) !== false) {
-            $table_name = $row_table['TABLE_NAME'];
-            $all_columns->execute();
-
-            // Сбор всех колонок таблицы
-            $table_columns = [];
-            while (($row_table_columns = $all_columns->fetch()) !== false) {
-                $table_columns[] = new ModelGeneratorColumn(
-                    $row_table_columns['COLUMN_NAME'],
-                    $row_table_columns['COLUMN_KEY'] === 'PRI',
-                    $row_table_columns['COLUMN_COMMENT'],
-                    $row_table_columns['IS_NULLABLE'] === 'YES'
-                );
-            }
-
-            // Добавление таблицы в массив
-            $tables[] = new ModelGeneratorTable($table_name, $table_columns);
-        }
-
-        // Генерация кода
-        define('models_dir', rootdir.'src/Models/');
-        foreach ($tables as $table) {
-            echo "===Создание файлов для таблицы: ".$table->getName()."===\n";
-
-            $class_filename = models_dir.$table->getClassFileName();
-            $trait_filename = models_dir.$table->getTraitFileName();
-
-            if (file_exists($class_filename)) {
-                echo "- Класс уже существует и не будет перезаписан\n";
-            } else {
-                $fp = fopen($class_filename, 'w');
-                fwrite($fp, $table->getClassCode(self::$class_template));
-                fclose($fp);
-                echo "- Класс сгенерирован\n";
-            }
-
-            $fp = fopen($trait_filename, 'w');
-            fwrite($fp, $table->getTraitCode(self::$trait_template));
-            fclose($fp);
-            echo "- Трейт сгенерирован\n";
-
-            echo "Успешно сгенерировано\n\n";
-        }
-    }
-}

+ 0 - 41
tools/ModelGeneratorColumn.php

@@ -1,41 +0,0 @@
-<?php
-
-namespace BotKit\Tools;
-
-// Колонка для генератора кода
-
-class ModelGeneratorColumn {
-
-	private string $name;
-	private bool $is_primary;
-	private string $comment;
-    private bool $is_nullable;
-
-	public function __construct($name, $is_primary, $comment, $is_nullable) {
-		$this->name = $name;
-		$this->is_primary = $is_primary;
-		$this->comment = $comment;
-        $this->is_nullable = $is_nullable;
-	}
-
-	public function isPrimary() : bool {
-		return $this->is_primary;
-	}
-
-    public function isNullable(): bool {
-        return $this->is_nullable;
-    }
-
-	// Возвращает название колонки, но добавляет знак доллара перед ним
-	public function getNameAsVariable() : string {
-		return '$'.$this->name;
-	}
-
-	public function getName() : string {
-		return $this->name;
-	}
-
-	public function getComment() : string {
-		return $this->comment;
-	}
-}

+ 0 - 112
tools/ModelGeneratorTable.php

@@ -1,112 +0,0 @@
-<?php
-
-namespace BotKit\Tools;
-
-// Таблица БД для генератора кода
-/* Таблицу следует создавать в единственном числе, т.к. класс создастся с
-названием как раз имени. (Например: Для таблицы 'user' будет создан класс
-'User') */
-
-class ModelGeneratorTable {
-    private string $name;
-    private array $columns;
-
-    public function __construct(string $name, array $columns) {
-        $this->name = $name;
-        $this->columns = $columns;
-    }
-
-    // Возвращает название файла для класса
-    public function getClassFileName() {
-        return $this->getName().'Model.php';
-    }
-
-    // Возвращает название файла для трейта
-    public function getTraitFileName() {
-        return $this->getName().'Trait.php';
-    }
-
-    // Возвращает имя таблицы, переводит snake_case в CamelCase
-    public function getName() {
-        return str_replace('_', '', ucwords($this->name, '_'));
-    }
-
-    public function getTraitCode($template_string) : string {
-
-        $create_params = '';
-        $object_binds = '';
-        $params_binds = '';
-        $comments = '';
-        $insert_columns = '';
-        $insert_values = '';
-        $update_sets = '';
-        $max_column_length = 0;
-        
-        // Генерация параметров для функций
-        $last_index = array_key_last($this->columns);
-        foreach ($this->columns as $column_index => $column) {
-            // Первичные колонки заполняются СУБД
-            if ($column->isPrimary()) {
-                continue;
-            }
-            $column_name = $column->getName();
-            $max_column_length = max(mb_strlen($column_name), $max_column_length);
-
-            $create_params .= "\$$column_name";
-
-            $object_binds .= "        \$statement->bindValue(':$column_name', \$object['$column_name']);\n";
-            $params_binds .= "        \$statement->bindValue(':$column_name', \$$column_name);\n";
-
-            $insert_columns .= $column_name;
-            $insert_values .= ":$column_name";
-            $update_sets .= $column_name.'=:'.$column_name;
-
-            if ($column_index != $last_index) {
-                $insert_columns .= ',';
-                $insert_values .= ',';
-                $update_sets .= ',';
-                $create_params .= ',';
-            }
-        }
-
-        // Генерация комментариев
-        $comment_line = "    // %' -".($max_column_length + 1)."s: %s\n";
-        foreach ($this->columns as $column) {
-            if ($column->isPrimary()) {
-                $col_comment = 'Первичный ключ';
-            } else {
-                $col_comment = $column->getComment();
-                if (strlen($col_comment) == 0) {
-                    $col_comment = 'Комментарий не указан';
-                }
-            }
-
-            $comments .= sprintf($comment_line,
-                $column->getName(),
-                $col_comment
-            );
-        }
-
-        return sprintf($template_string,
-            $this->getName().'Trait',   // Название трейта
-            $this->name,                // Название таблицы
-            $comments,                  // Пояснения колонок
-            $create_params,             // Параметры функции create()
-            $this->name,                // В какую таблицу вставляется запись при create()
-            $insert_columns,            // Колонки запроса вставки
-            $insert_values,             // Значения запроса вставки
-            $params_binds,              // Код привязки параметров (create)
-            $this->name,                // Название таблицы для CREATE
-            $this->name,                // Название таблицы для UPDATE
-            $update_sets,               // Запрос обновления (SET x=:x, y=:y...)
-            $object_binds               // Код привязки параметров (update)
-        );
-    }
-
-    public function getClassCode($template_string) : string {
-        return sprintf($template_string,
-            $this->getName(),
-            $this->getName().'Trait'
-        );
-    }
-}

+ 0 - 14
tools/generateModels.php

@@ -1,14 +0,0 @@
-<?php
-
-namespace BotKit\Tools;
-
-/* Этот скрипт получает информацию из базы данных о таблицах и их колонках
-и на основании этого создаёт файлы моделей. На самом деле создаются трейты.
-В самом классе модели указывается, что модель наследуется от абстрактного
-BotKit\Models\Model и используется трейт, который был сгенерирован этим
-скриптом. Таким образом изменения, внесённые разработчиком не перезапишутся,
-если схема БД обновится и потребуется обновление. */
-
-require_once __DIR__.'/../src/bootstrap.php';
-
-ModelCodeGenerator::run($_ENV['db_user'], $_ENV['db_password'], $_ENV['db_name']);