jemdev / dbrm
数据库关系映射,访问库。
Requires
- php: >=5.4.0
- hoa/registry: ~3.0
This package is auto-updated.
Last update: 2024-09-04 02:37:24 UTC
README
- 作者:Jean Molliné
- 许可证:CeCILL V2
- 先决条件
- PHP >= 5.4
- 使用:Hoa\Registry
- 联系:消息
- Github:github.com/jemdev/dbrm
- Packagist:packagist.org/packages/jemdev/dbrm
安装
使用 Composer,在 composer.json 的 require 部分添加以下内容
{
"jemdev/dbrm": "dev-master"
}
介绍和运行原理。
该包允许访问关系型数据库的数据。其核心思想是,可以进行多表的读取,但只能一次在单个表上执行写入。因此,可以创建动态对象以进行写入操作的每个表。
一些相对简单的方法可以执行准备好的查询以收集数据。对于写入,其他方法允许创建一个实例以初始化给定表的行,并将所需的值分配给该行的不同列。根据是否提供了行标识符,写入将是一个创建、修改或删除。
为了能够创建这些动态实例,一个系统可以建立一种数据模式映射,详细列出表、关系表和视图。基于这些信息,为给定表定义的实例通过读取列列表、它们的类型和其他实用信息来设置属性。
在连接时,如果配置文件不存在,它将被自动创建。随后,如果修改了模式结构,即使是仅添加、修改或从表中删除列,都有一个方法可以重新生成该配置文件。发现每次需要为每个表创建一个类非常不方便,这些修改导致某些类需要部分重写。因此,这些类是动态管理的,实际上它们是虚拟类。
获取连接对象
配置连接
必须创建一个包含 SGBDR 连接参数的文件。该文件必须命名为 dbCnxConf.php,并按照以下格式进行格式化
<?php /** * Fichier de configuration des paramètres de connexion à la base de données. * Ce fichier est généré automatiquement lors de la phase initiale d'installation. */ $db_app_server = 'localhost'; // Adresse du serveur de base de données $db_app_schema = 'testjem'; // Schéma à cartographier (base de données de l'application) $db_app_user = 'testjem'; // Utilisateur de l'application pouvant se connecter au SGBDR $db_app_mdp = 'testjem'; // Mot-de-passe de l'utilisateur de l'application $db_app_type = 'pgsql'; // Type de SGBDR, MySQL, PostGreSQL, Oracle, etc.. $db_app_port = '5432'; // Port sur lequel on peut se connecter au serveur $db_meta_schema = 'INFORMATION_SCHEMA'; // Schéma où pourront être collectées les informations sur le schéma de travail /** * Création des constantes globales de l'application * NE PAS MODIFIER LES LIGNES SUIVANTES */ defined("DB_ROOT_SERVER") || define("DB_ROOT_SERVER", $db_app_server); defined("DB_ROOT_USER") || define("DB_ROOT_USER", $db_app_user); defined("DB_ROOT_MDP") || define("DB_ROOT_MDP", $db_app_mdp); defined("DB_ROOT_SCHEMA") || define("DB_ROOT_SCHEMA", $db_meta_schema); defined("DB_ROOT_TYPEDB") || define("DB_ROOT_TYPEDB", $db_app_type); defined("DB_ROOT_DBPORT") || define("DB_ROOT_DBPORT", $db_app_port); defined("DB_APP_SCHEMA") || define("DB_APP_SCHEMA", $db_app_schema); defined("DB_APP_SERVER") || define("DB_APP_SERVER", $db_app_server); defined("DB_APP_USER") || define("DB_APP_USER", $db_app_user); defined("DB_APP_PASSWORD") || define("DB_APP_PASSWORD", $db_app_mdp);
支持的 SGBDR 类型
目前,仅可使用 MySQL 和 PostGreSQL。我没有测试过除了 MariaDb 以外的 MySQL 分支(Percona 等),但由于它们是兼容的,所以不应出现障碍。用于变量 $db_app_type 的值
- MySQL:mysql(适用于此类型的 MariaDB)
- PostGreSQL:pgsql
此文件应放置在您的配置文件所在目录中,根据应用程序的架构。随后,您将能够提供指向此文件的绝对路径。如果一开始不存在,则会自动生成另一个配置文件,这对于包的正常运行是必不可少的。此文件将生成两个版本,第一个命名为 dbConf.php,任何开发者都很容易阅读,第二个命名为 dbConf_compact.php,是供应用程序使用的主要版本,内容完全相同但已压缩成一行。该文件详细描述了整个数据结构,包括表、关系表和视图、列、键和其他详细信息。它将由所有用于写入、插入、更新或删除操作的对象使用。
可访问的全局方法
以下两种基本方法是必不可少的
- setRequete($sql, $aParams = array(), $cache = true) 定义要执行的查询,可选地,可以在关联数组中指定参数,其中每个索引是sql变量的名称,该变量将分配给相应的值,第三个参数允许启用结果的缓存,缓存默认是关闭的;
- execute() 此方法可以直接执行使用 setRequete() 定义的函数。然后可以发送查询、存储过程调用或用户函数,甚至可以是写入查询,尽管不推荐使用此选项(请参阅下文的数据写入)
以下两种方法在这里特别重要,并且仅在需要记录数据创建或修改时使用
- startTransaction() 如果表使用事务性引擎,则开始事务。然后所有后续的查询都将包含在事务中,直到调用 finishTransaction() 方法;
- finishTransaction($bOk) 结束事务:期望的参数是布尔值,TRUE 将执行 COMMIT,FALSE 将执行 ROLLBACK;
在应用程序开发阶段,另一种方法可能会很有用
- getErreurs() 以数组的形式返回遇到的错误列表
数据读取
目前没有查询生成器。我们需要自己编写查询,这些查询将被执行以收集数据。
每个查询都可以参数化,并使用 PDO 执行:它将返回单个数据、数据行或数据表,甚至可以是对象。我们将依赖于预先定义的 jemdev\dbrm\vue 类的实例。
示例:按照惯例,连接实例将是变量“$oVue”,并且已经在上文中定义(请理解“视图”一词在 SQL 中的含义)。
<?php /* Définition de la requête */ $sql = "SELECT utl_id, utl_nom, utl_prenom, utl_dateinscription ". "FROM t_utilisateur_utl ". "WHERE utl_dateinscription > :p_utl_dateinscription ". "ORDER BY utl_nom, utl_prenom"; /* Initialisation de paramètre(s) */ $params = array(':p_utl_dateinscription' => '2015-10-15'); /* Initialisation de la requête */ $oVue->setRequete($sql, $params); /* Récupération des données */ $infosUtilisateur = $oVue->fetchAssoc();
注意
在此示例中,$oVue 对象的名称并非无关紧要。这里所说的“视图”一词是指 SQL 中的含义。数据库中的视图是查询的摘要。它可以被视为一个虚拟表,由一个查询定义。
可访问的方法
方法名称借鉴了与 MySQL 扩展一起使用的方法。返回值在形式上也是相似的。
- fetchAssoc() 返回一个关联数组的结果,其中索引是查询中确定的列名或别名;
- fetchArray() 返回一个数组,其中每列都由两个索引表示,一个是数字,另一个是关联的,表示列名;
- fetchObject() 返回一个对象,其中每列都是一个属性;
- fetchLine($out = 'array') 返回一行数据。可以通过传递以下常量之一来指定结果的形式
- vue::JEMDB_FETCH_OBJECT = 'object' : 返回对象形式的数据;
- vue::JEMDB_FETCH_ARRAY = 'array' : 返回包含数字和关联索引的数组;
- vue::JEMDB_FETCH_ASSOC = 'assoc' : 返回关联数组;
- vue::JEMDB_FETCH_NUM = 'num' : 返回数字索引的数组;
- fetchOne() 返回唯一数据;
数据写入
可访问的方法
在给定实例上,您有如下方法
- init($aPk = null) : 初始化行实例。在后台,对象将根据目标表的列动态构建属性;
- sauvegarder() : 根据是否确定了主键,通过INSERT或UPDATE请求记录对属性的修改;
- supprimer() : 通过DELETE请求从表中删除行。如果使用事务引擎并且定义了参照完整性约束(CONSTRAINT),当存在引用要删除行的相关表中的数据时,此方法可能会返回错误;
- startTransaction() : 启动SQL事务;
- finishTransaction($bOk) : 结束SQL事务,期望参数是一个布尔值,指示是否执行COMMIT或ROLLBACK;
实践应用
如引言所述,我们一次写入一个表的数据。为此,我们创建一个表示该表行的对象。以下是一个示例:我们示例所依赖的表将具有以下形式
- 表 t_interlocuteur_int
+---------------------+-------------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------------+-------------------------+------+-----+---------+----------------+
| int_id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| adr_id | int(10) unsigned | YES | MUL | NULL | |
| int_nom | varchar(255) | NO | | NULL | |
| int_prenom | varchar(255) | NO | | NULL | |
| int_dateinscription | date | YES | | NULL | |
+---------------------+-------------------------+------+-----+---------+----------------+
- 表 t_adresse_adr
+-------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+------------------+------+-----+---------+----------------+
| adr_id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| adr_numero | varchar(16) | YES | | NULL | |
| adr_libelle_1 | varchar(128) | NO | | NULL | |
| adr_libelle_2 | varchar(128) | YES | | NULL | |
| adr_codepostal | varchar(16) | NO | | NULL | |
| adr_commune | varchar(128) | NO | | NULL | |
+-------------------+------------------+------+-----+---------+----------------+
重要提示 : 您无法在插入新行数据时自行定义主键的值。此值将由数据库管理系统(SGBD)自动生成,前提是该列已设置为 auto-increment
(MySQL和某些SGBDR。对于没有此功能的数据库(如Oracle),建议为每个表设置一个序列和一个 before insert 触发器,以自动生成主键)。当初始化数据行时,如果知道主键值,可以选择指定该值:如果指定了该值,则将使用相应的值填充列,否则,我们将得到一个空行,准备好进行填充。现在,让我们写入一行数据
<?php /* On crée l'instance de la ligne à partir du nom de la table cible */ $oAdresse = $oDbrm->getLigneInstance('t_adresse_adr'); /* * On détermine si l'on dispose ou non de la clé primaire de la ligne * et on stocke ça dans un tableau associatif */ $aPk = (!empty($adr_id)) ? array('adr_id' => $adr_id) ? null; /* On initialise l'instance */ $oAdresse->init($aPk); /* * Dès cet instant, notre objet présente chaque colonne de la * table t_interlocuteur_int comme des propriétés qu'on peut modifier */ $oAdresse->adr_numero = $adr_numero; $oAdresse->adr_libelle_1 = $adr_libelle_1; // ... etc... /* On peut maintenant sauvegarder ces informations */ $enreg = $oAdresse->sauvegarder(); /* * Terminé, les écritures pour cette ligne sont terminées. * On peut récupérer la valeur de la clé primaire si nécessaire et s'il * s'agissait d'une création. Cette clé primaire est automatiquement gérée * et initialisée dans l'instance. * S'il y a eu une erreur, la méthode sauvegarder retournera l'erreur, sinon * elle retournera TRUE */ if(true == $enreg) { /* * Ici, si par exemple vous avez d'autres données à enregistrer, données qui * dépendent la la réussite de ce premier enregistrement, vous continuez * sur l'enregistrement suivant, exemple : */ $adr_id = $oAdresse->adr_id; $oInterlocuteur = $oDbrm->getLigneInstance('t_interlocuteur_int'); /* * On détermine si l'on dispose ou non de la clé primaire de la ligne * et on stocke ça dans un tableau associatif */ $aPk = (!empty($int_id)) ? array('int_id' => $int_id) : null; /* On initialise l'instance */ $oInterlocuteur->init($aPk); /* * Dès cet instant, notre objet présente chaque colonne de la * table t_interlocuteur_int comme des propriétés qu'on peut modifier */ $oInterlocuteur->adr_id = $adr_id; // Ici, on alimente la clé étrangère définie en enregistrant l'adresse. $oInterlocuteur->int_nom = $int_nom; $oInterlocuteur->int_prenom = $int_prenom; if(!is_null($int_dateinscription)) { $oInterlocuteur->int_dateinscription = $int_dateinscription; } /* On peut maintenant sauvegarder ces informations */ $enreg = $oInterlocuteur->sauvegarder(); // etc... suite selon les besoins. } else { // Ici, le code permettant la gestion de l'erreur selon vos propres manières de faire. }
可能出现的错误
在插入数据时,将自动执行检查。如果某个列必须要有值而未提供,将引发异常。数据类型也将进行检查,并且如果数据不符合定义的数据模型,则可能会引发异常。
总结:表的实例
当您为某个表创建一个实例时,该表的每一列都成为该实例的属性。因此,列 int_nom
是对象 $oInterlocuteur
的属性,因此可以像公开对象属性一样调用它。如果您尝试将值分配给在考虑的表中不存在的列,将引发异常。同样,您不能随意分配主键的值。但是,在涉及关系表复合键的情况下,存在可以自行定义主键值的情况。因此,如果我们有如示例中的直接联系人和地址之间的关系表(例如 r_int_has_adr_iha
),并且该关系表的主键由列 int_id
和 adr_id
组成,我们将按以下方式修改前面的代码
<?php /* Définition des valeurs de bases des identifiants */ $int_id = null; $adr_id = null; /* On crée l'instance de la ligne à partir du nom de la table cible */ $oAdresse = $oDbrm->getLigneInstance('t_adresse_adr'); /* * On détermine si l'on dispose ou non de la clé primaire de la ligne * et on stocke ça dans un tableau associatif */ $aPk = (!empty($adr_id)) ? array('adr_id' => $adr_id) ? null; /* On initialise l'instance */ $oAdresse->init($aPk); /* * Dès cet instant, notre objet présente chaque colonne de la * table t_interlocuteur_int comme des propriétés qu'on peut modifier */ $oAdresse->adr_numero = $adr_numero; $oAdresse->adr_libelle_1 = $adr_libelle_1; // ... etc... /* On peut maintenant sauvegarder ces informations */ $enreg = $oAdresse->sauvegarder(); /* * Terminé, les écritures pour cette ligne sont terminées. * On peut récupérer la valeur de la clé primaire si nécessaire et s'il * s'agissait d'une création. Cette clé primaire est automatiquement gérée * et initialisée dans l'instance. * S'il y a eu une erreur, la méthode sauvegarder retournera l'erreur, sinon * elle retournera TRUE */ if(true == $enreg) { $adr_id = $oAdresse->adr_id; // etc... suite selon les besoins. } else { // Ici, le code permettant la gestion de l'erreur selon vos propres manières de faire. } $oInterlocuteur = $oDbrm->getLigneInstance('t_interlocuteur_int'); /* * On détermine si l'on dispose ou non de la clé primaire de la ligne * et on stocke ça dans un tableau associatif */ $aPk = (!empty($int_id)) ? array('int_id' => $int_id) : null; /* On initialise l'instance */ $oInterlocuteur->init($aPk); /* * Dès cet instant, notre objet présente chaque colonne de la * table t_interlocuteur_int comme des propriétés qu'on peut modifier */ $oInterlocuteur->adr_id = $adr_id; // Ici, on alimente la clé étrangère définie en enregistrant l'adresse. $oInterlocuteur->int_nom = $int_nom; $oInterlocuteur->int_prenom = $int_prenom; if(!is_null($int_dateinscription)) { $oInterlocuteur->int_dateinscription = $int_dateinscription; } /* On peut maintenant sauvegarder ces informations */ $enreg = $oInterlocuteur->sauvegarder(); if(true == $enreg) { $int_id = $oInterlocuteur->int_id; // etc... suite selon les besoins. } else { // Ici, le code permettant la gestion de l'erreur selon vos propres manières de faire. } if(!is_null($int_id) && !is_null($adr_id)) { /* Maintenant, on peut alimenter la tablea relationnelle */ $oAdresseInt = $oDbrm->getLigneInstance('r_int_has_adr_iha'); /* On définit les éléments de la clé primaire composite */ $aPk = array( 'int_id' => $int_id, 'adr_id' => $adr_id ); /* On initialise l'instance de l'objet */ $oAdresseInt->init($aPk); /* On peut sauvegarder */ $enreg = $oAdresseInt->sauvegarder(); if(true !== $enreg) { // Ici, le code permettant la gestion de l'erreur selon vos propres manières de faire. } }
然后,在分配主键值时没有发生阻塞。
在实践中
实际使用可能会引导您将写操作请求分散到不同的表,这些表在不同的函数/方法中被调用,从单一位置调用。这样,您可以在需要时使用事务模式。开始事务时,执行每个记录,如果某个方法因为错误返回 FALSE,您可以中断记录的连续性,并通过 ROLLBACK 完成事务,从而避免用孤立的或不一致的数据污染您的表。
(目前)无法做的事情
目前,还有一些元素在 待办事项列表 中,特别是在写入数据时,不能赋值一个值,而是一个 SQL 函数的调用。例如,如果您想使用 MySQL 内置的加密函数来赋值,目前还不能这样做。
$instanceLigne->nom_colonne = "AES_ENCRYPT('valeur', 'Clé de chiffrement')";
如何绕过这个问题。
对于日常使用来说,这并不是一个真正的问题,这种特定情况相对较少。如果您确实需要执行此类操作,您有两个选择。
- 第一个是通过发送明文值,并在表上添加一个 BEFORE INSERT 触发器,该触发器将执行要应用到值上的 SQL 函数以将其赋值给列。但这种方法可能会在默认禁用用户函数、存储过程和触发器的共享服务器上被阻塞;
- 第二个是您自己编写写操作 INSERT 或 UPDATE 请求,并使用以下方式通过
execute()
方法执行
<?php /* On a d'abord besoin d'une instance de jemdev\dbrm\vue */ $oVue = $oDb->getDbrmInstance(); /* On définit la requête SQL d'insertion */ $sql = "INSERT INTO matable (col_login, col_motdepasse)". "VALUES('Toto', AES_ENCRYPT('valeur', 'Clé de chiffrement'))"; $oVue->setRequete($sql); /* Exécution de la requête. */ $enreg = $oVue->execute();
代码的其余部分没有变化。
一个实例 = 一行
目前尚未计划实现多行更新或删除的功能,因为更新将仅根据主键值进行。多年来我一直在这个管理应用中使用这个包,实际上我从未需要实现这个功能。而且对于偶尔需要这种情况,我可以通过收集要考虑的主键列表来解决这个问题,并在循环中逐行处理。
查询执行时间
这个小系统依赖于 PHP 的原生 error_log
方法来记录慢查询。在开发应用程序的过程中,识别减慢应用程序的瓶颈可能很有用。我们可以简单地激活一个测量系统,该系统将自动计时所有查询。我们可以通过以下方式配置系统:
- 日志记录类型:“php”、“文件”或“电子邮件”;
- 从哪个最小秒数开始记录请求;
- 可选:如果已定义“文件”模式,记录信息到的绝对文件路径;
- 可选:如果已定义“电子邮件”模式,将警告消息发送到的电子邮件地址。
实现非常简单,例如
$mode = 'fichier'; $maxtime = 1; $fichier = 'app/tmp/logs/journaldb.log'; $this->_oDb->activerModeDebug($mode, $maxtime, $fichier);
就是这样:一旦启动,只需正常浏览应用程序,特别是显示明显延迟的部分,然后检查日志文件以查找可能需要优化以加快速度的查询。
对于“文件”模式,如果文件不存在,将会创建。
动态缓存管理
一个访问我在其上工作的 MySQL 服务器配置的问题阻止了我配置内置缓存甚至简单地将其默认启用。我希望能够有一个请求缓存管理系统,因此我添加了用于管理此方面的类。
总体来说,每个读取请求在缓存激活的情况下,可以将结果存储在文件缓存中,甚至如果启用了 MemCache
扩展,还可以存储在 MemCache
中。对任何表的写入操作都会重新生成涉及该表的请求的缓存。因此,缓存的有效期取决于新的写入操作,而不是预定义的持续时间。如果请求的结果在三个分钟内有效,并且发生了写入操作,则缓存会更新;如果相同的在三个星期后仍然有效,那么重新生成它是完全没有必要的。
某些方法允许手动重新生成某些表的缓存。例如,如果在表的写入操作中触发器会触发执行存储过程,创建对其他表的写入,那么在这些其他表上重新生成缓存将是重要的。在 PHP 中无法检测这些写入操作,因为这是数据库管理系统直接处理的。同样,如果 CRON 作业触发写入操作而不通过 PHP,那么也无法拦截这些信息来更新相应的缓存,因此需要编写 PHP 代码来执行此清理,该代码需要在一个添加到 CronTab 中的任务中执行。
默认情况下,缓存未激活,如果您有管理您的关系型数据库管理系统(RDBMS)内置缓存的能力,那么这将是一个更优且性能更好的解决方案。
Symfony 集成
对于那些想使用 Symfony 并获取数据库数据而不使用 Doctrine 的人来说,这个解决方案提供了一个可行的替代方案,尽管技术上可能要求更高。这里,不能使用这个库来修改数据库结构:这是数据库管理员(DBA)的职责,并且不允许开发者修改这个结构。然而,jemdev\dbrm 系统本身允许数据结构的变化,并且必须生成一个配置文件。Symfony 的命令行界面(CLI)系统对于此非常有用。因此,我们将查看如何通过单行命令生成此文件。
注意:以下仅是一个可行的建议,您可以选择以其他方式实现。
配置文件
在 Symfony 应用程序的 /config 目录中,我们将在 /config/packages 中创建一个名为 /dbrm 的子目录。
在这个新目录中,我们将创建两个基本文件,appConf.php 和 dbConf.php,并记录一些基本元素以使配置系统生效。
appConf.php 文件
<?php $mode = (defined('APP_ENV')) ? APP_ENV : 'dev'; defined("MODE") || define("MODE", $mode); defined("DS") || define("DS", DIRECTORY_SEPARATOR); defined("REP_ROOT") || define("REP_ROOT", dirname(dirname(dirname(__DIR__))). DS); /** * Paramètres de connexion root pour pouvoir récupérer si nécessaire * les informations de base de données et construire le fichier dbConf.php */ $db_server_type = 'mysql'; switch (MODE) { case 'dev': $db_app_server = 'localhost'; $db_app_port = '3306'; $db_app_schema = 'monshema'; $db_app_user = 'monutilisateur'; $db_app_pwd = 'abc123'; $db_bin_path = '/var/lib/mysql/bin'; $db_root_schema = "INFORMATION_SCHEMA"; break; case 'test': $db_app_server = 'localhost'; $db_app_port = '3306'; $db_app_schema = 'monshematest'; $db_app_user = 'monutilisateurtest'; $db_app_pwd = 'abc123test'; $db_bin_path = '/var/lib/mysql/bin'; $db_root_schema = "INFORMATION_SCHEMA"; break; case 'prd': case 'prod': $db_app_server = 'localhost'; $db_app_port = '3306'; $db_app_schema = 'monshema'; $db_app_user = 'monutilisateur'; $db_app_pwd = 'abc123'; $db_bin_path = '/var/lib/mysql/bin'; $db_root_schema = "INFORMATION_SCHEMA"; break; } defined("DB_ROOT_SERVER") || define("DB_ROOT_SERVER", $db_app_server); defined("DB_ROOT_USER") || define("DB_ROOT_USER", $db_app_user); defined("DB_ROOT_MDP") || define("DB_ROOT_MDP", $db_app_pwd); defined("DB_ROOT_SCHEMA") || define("DB_ROOT_SCHEMA", $db_root_schema); defined("DB_ROOT_TYPEDB") || define("DB_ROOT_TYPEDB", $db_server_type); defined("DB_ROOT_DBPORT") || define("DB_ROOT_DBPORT", $db_app_port); defined("DB_APP_SCHEMA") || define("DB_APP_SCHEMA", $db_app_schema); defined("DB_APP_SERVER") || define("DB_APP_SERVER", $db_app_server); defined("DB_APP_USER") || define("DB_APP_USER", $db_app_user); defined("DB_APP_PASSWORD") || define("DB_APP_PASSWORD", $db_app_pwd); defined("DB_BIN_PATH") || define("DB_BIN_PATH", $db_bin_path);
目前我们只需要这个文件中的内容。
dbConf.php 文件
此文件通常由包中的某个库生成,但通过提供基本框架,我们将简化这些事情。
<?php /** * @package jemdev * * Ce code est fourni tel quel sans garantie. * Vous avez la liberté de l'utiliser et d'y apporter les modifications * que vous souhaitez. Vous devrez néanmoins respecter les termes * de la licence CeCILL dont le fichier est joint à cette librairie. * {@see http://www.cecill.info/licences/Licence_CeCILL_V2-fr.html} * * Date de génération du fichier : 12/05/2023 11:05:36 */ /** * Définition des constantes sur les types de données */ defined('TYPE_INTEGER') || define('TYPE_INTEGER', 'INT'); defined('TYPE_VARCHAR') || define('TYPE_VARCHAR', 'VARCHAR'); defined('TYPE_ENUM') || define('TYPE_ENUM', 'ENUM'); defined('TYPE_FLOAT') || define('TYPE_FLOAT', 'FLOAT'); defined('TYPE_TINYINT') || define('TYPE_TINYINT', 'TINYINT'); defined('TYPE_DATE') || define('TYPE_DATE', 'DATE'); defined('TYPE_MEDIUMINT') || define('TYPE_MEDIUMINT', 'MEDIUMINT'); defined('TYPE_BLOB') || define('TYPE_BLOB', 'BLOB'); /** * Description détaillée des schémas */ $dbConf = array( 0 => array( 'schema' => array( 'name' => DB_APP_SCHEMA, 'SGBD' => DB_ROOT_TYPEDB, 'server' => DB_APP_SERVER, 'port' => DB_ROOT_DBPORT, 'user' => DB_APP_USER, 'mdp' => DB_APP_PASSWORD, 'pilote' => DB_ROOT_TYPEDB ), 'tables' => array(), 'relations' => array(), 'vues' => array() ) );
不要修改此基本框架。以后也永远不要手动修改此文件。
设置命令
在我们的类中,我们为命令定义了一个名称,“app:dbrmconf”。
现在打开控制台,在应用程序目录下,创建命令
$ php bin/console make:command app:dbrmconf
如果一切顺利,您将看到以下内容显示
created: src/Command/DbrmconfCommand.php
Success!
Next: open your new command class and customize it!
Find the documentation at https://symfony.com.cn/doc/current/console.html
因此,在 /src/Command 目录中已创建了一个类,我们将对其进行一些修改。编辑这个类。
命令类配置
以下是必要的代码
<?php namespace App\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use jemdev\dbrm\init\genereconf; use jemdev\dbrm\vue; /** * Commande personnalisée. * * Ce fichier va permettre de déclencher la (re)génération du fichier de configuration * de la base de données telle que requise par la librairie jemdev\dbrm * * En lançant simplement en console la commande suivante : * php bin/console app:dbrmconf * * @author JEM-Developpement Ltd */ class DbrmconfCommand extends Command { protected static $defaultName = 'app:dbrmconf'; protected static $defaultDescription = 'Génération du fichier de configuration de la base de données'; protected static $dirconf; protected function configure(): void { $this->setHelp('Cette commande permet de générer le fichier de configuration de la base de données pour le package jemdev\dbrm'); self::$dirconf = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR ."config". DIRECTORY_SEPARATOR ."packages". DIRECTORY_SEPARATOR ."dbrm". DIRECTORY_SEPARATOR; } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); include(self::$dirconf ."appConf.php"); $resetDbConf = $this->resetDbConf(DB_ROOT_USER, DB_ROOT_MDP); $msg = (true == $resetDbConf) ? "Terminé, le fichier a été correctement généré" : "Terminé avec des erreurs !"; $io->success($msg); return Command::SUCCESS; } public function resetDbConf($dbuser,$dbmdp) { include(self::$dirconf ."appConf.php"); // Création du fichier de configuration de la base. $cible = REP_ROOT . DIRECTORY_SEPARATOR ."config". DIRECTORY_SEPARATOR ."packages". DIRECTORY_SEPARATOR ."dbrm". DIRECTORY_SEPARATOR ."dbConf.php"; $oDbInit = new genereconf(DB_APP_SCHEMA,DB_ROOT_USER,DB_ROOT_MDP,$dbuser,$dbmdp,DB_ROOT_TYPEDB,DB_ROOT_SERVER,DB_ROOT_DBPORT); $aConfIS = array( 'schema' => array( 'name' => DB_ROOT_SCHEMA, 'SGBD' => DB_ROOT_TYPEDB, 'server' => DB_ROOT_SERVER, 'port' => DB_ROOT_DBPORT, 'user' => DB_ROOT_USER, 'mdp' => DB_ROOT_MDP, 'pilote' => DB_ROOT_TYPEDB ), 'tables' => array(), 'relations' => array(), 'vues' => array() ); $oVue = vue::getInstance($aConfIS); $bGenConf = $oDbInit->genererConf($oVue, $cible); return $bGenConf; } }
DbrmconfCommand::execute 方法将启动我们添加的 DbrmconfCommand::resetDbConf 方法的执行,并且上述 dbConf.php 文件将根据在数据库中收集到的信息重新生成配置文件。
我们通过请求现有命令的列表来验证这个新命令的存在,但限制在 app 命名空间中。
$ php bin/console list app
正常结果应该包含以下内容
. . . .
Available commands for the "app" namespace:
app:dbrmconf Génération du fichier de configuration de la base de données
我们可以在底部清楚地看到我们新的命令,以及它所携带的消息,告知了该命令的性质,正如我们在新类中定义的那样。
让我们试试看
$ php bin/console app:dbrmconf
如果一切顺利,你应该会看到以下内容
[OK] Terminé, le fichier a été correctement généré
正如我们在命令类中的 execute 方法所定义的那样。
注意,jemdev\dbrm 不是 Doctrine
此包永远不会取代 Doctrine,它不是 ORM。它可以收集数据、修改或删除数据。但它不允许修改数据结构,也不允许生成预填充表单和其他类似功能。它也不强制使用 PHP 和 SQL 之间的某种混合语言:如果你没有基本的 SQL 语言掌握,请继续使用 Doctrine。
结论
此包的设计旨在易于使用,以便开发者不会陷入实现复杂性中,同时无需关心所使用的数据库服务器类型,无论是 MySQL/MariaDb 还是 PostgreSQL。
即将推出
还有待开发代码,以支持除 MySQL 或 PostgreSQL 之外的其他数据库管理系统,这些代码目前还不存在。这是构建数据模式配置表的代码。MySQL 和 PostgreSQL 实现了 INFORMATION_SCHEMA,这极大地简化了这项工作,但并非所有数据库管理系统都实现了它,例如 Oracle。然而,有其他方法可以收集这些信息,以达到相同的结果。
随后,基于 PDO 的功能,jemdev\dbrm 的集成可以在任何项目中完成。
长期项目
自动生成查询的想法已经存在了一段时间,但遗憾的是我在这方面没有数学知识。这涉及到基于图论来确定需要建立哪些连接,以避免定义表中的列,从而使引擎自动构建正确的路径。配置文件已经可以创建一个矩阵(代码尚未集成到包中,但已经准备好并可以运行),现在需要定义适当的算法来构建符合最高标准的查询。
欢迎对此做出贡献。