mirror of
https://codeberg.org/artfulrobot/contactcats.git
synced 2025-06-28 18:57:18 +02:00
checkin (broken)
This commit is contained in:
parent
a01abba787
commit
06fec5daf7
21 changed files with 2025 additions and 376 deletions
232
mixin/lib/civimix-schema@5.80.2/src/SqlGenerator.php
Normal file
232
mixin/lib/civimix-schema@5.80.2/src/SqlGenerator.php
Normal file
|
@ -0,0 +1,232 @@
|
|||
<?php
|
||||
/*
|
||||
+--------------------------------------------------------------------+
|
||||
| Copyright CiviCRM LLC. All rights reserved. |
|
||||
| |
|
||||
| This work is published under the GNU AGPLv3 license with some |
|
||||
| permitted exceptions and without any warranty. For full license |
|
||||
| and copyright information, see https://civicrm.org/licensing |
|
||||
+--------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
return new class() {
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $entities;
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $findExternalTable;
|
||||
|
||||
/**
|
||||
* @param string $module
|
||||
* Ex: 'civicrm' or 'org.example.mymodule'
|
||||
* @param string $path
|
||||
* Ex: '/var/www/sites/all/modules/civicrm/schema'
|
||||
* @param bool $isolated
|
||||
* TRUE if these entities should be a self-sufficient (i.e. no external references).
|
||||
* FALSE if these entities may include references to other tables.
|
||||
* TRUE would make sense in (eg) civicrm-core, before installation or bootstrap
|
||||
* FALSE would make sense in (eg) an extension on an active system.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function createFromFolder(string $module, string $path, bool $isolated) {
|
||||
$files = \CRM_Utils_File::findFiles($path, '*.entityType.php');
|
||||
$entities = [];
|
||||
foreach ($files as $file) {
|
||||
$entity = include $file;
|
||||
$entity['module'] = $module;
|
||||
$entities[$entity['name']] = $entity;
|
||||
}
|
||||
|
||||
$findExternalTable = $isolated ? NULL : (['CRM_Core_DAO_AllCoreTables', 'getTableForEntityName']);
|
||||
return new static($entities, $findExternalTable);
|
||||
}
|
||||
|
||||
public function __construct(array $entities = [], ?callable $findExternalTable = NULL) {
|
||||
// Filter out entities without a sql table (e.g. Afform)
|
||||
$this->entities = array_filter($entities, function($entity) {
|
||||
return !empty($entity['table']);
|
||||
});
|
||||
$this->findExternalTable = $findExternalTable ?: function() {
|
||||
return NULL;
|
||||
};
|
||||
}
|
||||
|
||||
public function getEntities(): array {
|
||||
return $this->entities;
|
||||
}
|
||||
|
||||
public function getCreateTablesSql(): string {
|
||||
$sql = '';
|
||||
foreach ($this->entities as $entity) {
|
||||
$sql .= $this->generateCreateTableSql($entity);
|
||||
}
|
||||
foreach ($this->entities as $entity) {
|
||||
$sql .= $this->generateConstraintsSql($entity);
|
||||
}
|
||||
return $sql;
|
||||
}
|
||||
|
||||
public function getCreateTableSql(string $entityName): string {
|
||||
$sql = $this->generateCreateTableSql($this->entities[$entityName]);
|
||||
$sql .= $this->generateConstraintsSql($this->entities[$entityName]);
|
||||
return $sql;
|
||||
}
|
||||
|
||||
public function getDropTablesSql(): string {
|
||||
$sql = "SET FOREIGN_KEY_CHECKS=0;\n";
|
||||
foreach ($this->entities as $entity) {
|
||||
$sql .= "DROP TABLE IF EXISTS `{$entity['table']}`;\n";
|
||||
}
|
||||
$sql .= "SET FOREIGN_KEY_CHECKS=1;\n";
|
||||
return $sql;
|
||||
}
|
||||
|
||||
public function generateCreateTableWithConstraintSql(array $entity): string {
|
||||
$definition = $this->getTableDefinition($entity);
|
||||
$constraints = $this->getTableConstraints($entity);
|
||||
$sql = "CREATE TABLE IF NOT EXISTS `{$entity['table']}` (\n " .
|
||||
implode(",\n ", $definition);
|
||||
if ($constraints) {
|
||||
$sql .= ",\n " . implode(",\n ", $constraints);
|
||||
}
|
||||
$sql .= "\n)\n" . $this->getTableOptions() . ";\n";
|
||||
return $sql;
|
||||
}
|
||||
|
||||
private function generateCreateTableSql(array $entity): string {
|
||||
$definition = $this->getTableDefinition($entity);
|
||||
$sql = "CREATE TABLE `{$entity['table']}` (\n " .
|
||||
implode(",\n ", $definition) .
|
||||
"\n)\n" .
|
||||
$this->getTableOptions() . ";\n";
|
||||
return $sql;
|
||||
}
|
||||
|
||||
private function getTableDefinition(array $entity): array {
|
||||
$definition = [];
|
||||
$primaryKeys = [];
|
||||
foreach ($entity['getFields']() as $fieldName => $field) {
|
||||
if (!empty($field['primary_key'])) {
|
||||
$primaryKeys[] = "`$fieldName`";
|
||||
}
|
||||
$definition[] = "`$fieldName` " . self::generateFieldSql($field);
|
||||
}
|
||||
if ($primaryKeys) {
|
||||
$definition[] = 'PRIMARY KEY (' . implode(', ', $primaryKeys) . ')';
|
||||
}
|
||||
$indices = isset($entity['getIndices']) ? $entity['getIndices']() : [];
|
||||
foreach ($indices as $indexName => $index) {
|
||||
$indexFields = [];
|
||||
foreach ($index['fields'] as $fieldName => $length) {
|
||||
$indexFields[] = "`$fieldName`" . (is_int($length) ? "($length)" : '');
|
||||
}
|
||||
$definition[] = (!empty($index['unique']) ? 'UNIQUE ' : '') . "INDEX `$indexName`(" . implode(', ', $indexFields) . ')';
|
||||
}
|
||||
return $definition;
|
||||
}
|
||||
|
||||
private function generateConstraintsSql(array $entity): string {
|
||||
$constraints = $this->getTableConstraints($entity);
|
||||
$sql = '';
|
||||
if ($constraints) {
|
||||
$sql .= "ALTER TABLE `{$entity['table']}`\n ";
|
||||
$sql .= 'ADD ' . implode(",\n ADD ", $constraints) . ";\n";
|
||||
}
|
||||
return $sql;
|
||||
}
|
||||
|
||||
private function getTableConstraints(array $entity): array {
|
||||
$constraints = [];
|
||||
foreach ($entity['getFields']() as $fieldName => $field) {
|
||||
if (!empty($field['entity_reference']['entity'])) {
|
||||
$constraint = "CONSTRAINT `FK_{$entity['table']}_$fieldName` FOREIGN KEY (`$fieldName`)" .
|
||||
" REFERENCES `" . $this->getTableForEntity($field['entity_reference']['entity']) . "`(`{$field['entity_reference']['key']}`)";
|
||||
if (!empty($field['entity_reference']['on_delete'])) {
|
||||
$constraint .= " ON DELETE {$field['entity_reference']['on_delete']}";
|
||||
}
|
||||
$constraints[] = $constraint;
|
||||
}
|
||||
}
|
||||
return $constraints;
|
||||
}
|
||||
|
||||
public static function generateFieldSql(array $field) {
|
||||
$fieldSql = $field['sql_type'];
|
||||
if (!empty($field['collate'])) {
|
||||
$fieldSql .= " COLLATE {$field['collate']}";
|
||||
}
|
||||
// Required fields and booleans cannot be null
|
||||
// FIXME: For legacy support this doesn't force boolean fields to be NOT NULL... but it really should.
|
||||
if (!empty($field['required'])) {
|
||||
$fieldSql .= ' NOT NULL';
|
||||
}
|
||||
else {
|
||||
$fieldSql .= ' NULL';
|
||||
}
|
||||
if (!empty($field['auto_increment'])) {
|
||||
$fieldSql .= " AUTO_INCREMENT";
|
||||
}
|
||||
$fieldSql .= self::getDefaultSql($field);
|
||||
if (!empty($field['description'])) {
|
||||
$fieldSql .= " COMMENT '" . \CRM_Core_DAO::escapeString($field['description']) . "'";
|
||||
}
|
||||
return $fieldSql;
|
||||
}
|
||||
|
||||
private static function getDefaultSql(array $field): string {
|
||||
// Booleans always have a default
|
||||
if ($field['sql_type'] === 'boolean') {
|
||||
$field += ['default' => FALSE];
|
||||
}
|
||||
if (!array_key_exists('default', $field)) {
|
||||
return '';
|
||||
}
|
||||
if (is_null($field['default'])) {
|
||||
$default = 'NULL';
|
||||
}
|
||||
elseif (is_bool($field['default'])) {
|
||||
$default = $field['default'] ? 'TRUE' : 'FALSE';
|
||||
}
|
||||
elseif (!is_string($field['default']) || str_starts_with($field['default'], 'CURRENT_TIMESTAMP')) {
|
||||
$default = $field['default'];
|
||||
}
|
||||
else {
|
||||
$default = "'" . \CRM_Core_DAO::escapeString($field['default']) . "'";
|
||||
}
|
||||
return ' DEFAULT ' . $default;
|
||||
}
|
||||
|
||||
private function getTableForEntity(string $entityName): string {
|
||||
return $this->entities[$entityName]['table'] ?? call_user_func($this->findExternalTable, $entityName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get general/default options for use in CREATE TABLE (eg character set, collation).
|
||||
*/
|
||||
private function getTableOptions(): string {
|
||||
if (!Civi\Core\Container::isContainerBooted()) {
|
||||
// Pre-installation environment ==> aka new install
|
||||
$collation = CRM_Core_BAO_SchemaHandler::DEFAULT_COLLATION;
|
||||
}
|
||||
else {
|
||||
// What character-set is used for CiviCRM core schema? What collation?
|
||||
// This depends on when the DB was *initialized*:
|
||||
// - civicrm-core >= 5.33 has used `CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`
|
||||
// - civicrm-core 4.3-5.32 has used `CHARACTER SET utf8 COLLATE utf8_unicode_ci`
|
||||
// - civicrm-core <= 4.2 -- I haven't checked, but it's probably the same.
|
||||
// Some systems have migrated (eg APIv3's `System.utf8conversion`), but (as of Feb 2024)
|
||||
// we haven't made any effort to push to this change.
|
||||
$collation = \CRM_Core_BAO_SchemaHandler::getInUseCollation();
|
||||
}
|
||||
|
||||
$characterSet = (stripos($collation, 'utf8mb4') !== FALSE) ? 'utf8mb4' : 'utf8';
|
||||
return "ENGINE=InnoDB DEFAULT CHARACTER SET {$characterSet} COLLATE {$collation} ROW_FORMAT=DYNAMIC";
|
||||
}
|
||||
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue