File indexing completed on 2024-12-15 03:45:06
0001 <?php 0002 /* 0003 SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org> 0004 0005 SPDX-License-Identifier: MIT 0006 */ 0007 0008 require_once('restexception.php'); 0009 require_once('utils.php'); 0010 0011 /** Represents a product schema entry element. */ 0012 class SchemaEntryElement 0013 { 0014 public $name; 0015 public $type; 0016 0017 private $schemaEntry = null; 0018 0019 const STRING_TYPE = 'string'; 0020 const INT_TYPE = 'int'; 0021 const NUMBER_TYPE = 'number'; 0022 const BOOL_TYPE = 'bool'; 0023 0024 public function __construct(SchemaEntry $entry) 0025 { 0026 $this->schemaEntry = &$entry; 0027 } 0028 0029 /** Checks if this schema entry element is valid. */ 0030 public function isValid() 0031 { 0032 if ($this->type != self::STRING_TYPE && $this->type != self::INT_TYPE && $this->type != self::NUMBER_TYPE && $this->type != self::BOOL_TYPE) 0033 return false; 0034 if (!Utils::isValidIdentifier($this->name)) 0035 return false; 0036 return true; 0037 } 0038 0039 /** Column name in the data table. */ 0040 public function dataColumnName() 0041 { 0042 $colName = 'col_data_' . Utils::normalizeString($this->schemaEntry->name) . '_' . Utils::normalizeString($this->name); 0043 return strtolower($colName); 0044 } 0045 0046 /** Insert this element into storage. */ 0047 public function insert(Datastore $db, $entryId) 0048 { 0049 $stmt = $db->prepare('INSERT INTO 0050 tbl_schema_element (col_schema_id, col_name, col_type) 0051 VALUES (:schemaId, :name, :type) 0052 '); 0053 $stmt->bindValue(':schemaId', $entryId, PDO::PARAM_INT); 0054 $stmt->bindValue(':name', $this->name, PDO::PARAM_STR); 0055 $stmt->bindValue(':type', $this->type, PDO::PARAM_STR); 0056 $db->execute($stmt); 0057 0058 if ($this->schemaEntry->isScalar()) 0059 $this->createScalarDataTableColumn($db); 0060 else 0061 $this->createNonScalarDataTableColumn($db); 0062 } 0063 0064 /** Delete this element from storage. */ 0065 public function delete(Datastore $db, $entryId) 0066 { 0067 $stmt = $db->prepare(' 0068 DELETE FROM tbl_schema_element 0069 WHERE col_schema_id = :schemaId AND col_name = :name 0070 '); 0071 $stmt->bindValue(':schemaId', $entryId, PDO::PARAM_INT); 0072 $stmt->bindValue(':name', $this->name, PDO::PARAM_STR); 0073 $db->execute($stmt); 0074 } 0075 0076 /** Drop data table column for this element. */ 0077 public function dropDataColumn(Datastore $db) 0078 { 0079 if ($db->driver() == 'sqlite') { 0080 error_log('Sqlite does not support dropping columns.'); 0081 return; 0082 } 0083 0084 try { 0085 if ($this->schemaEntry->isScalar()) { 0086 $stmt = $db->prepare('ALTER TABLE ' . $this->schemaEntry->product()->dataTableName() 0087 . ' DROP COLUMN ' . $this->dataColumnName()); 0088 $db->execute($stmt); 0089 } else { 0090 $stmt = $db->prepare('ALTER TABLE ' . $this->schemaEntry->dataTableName() 0091 . ' DROP COLUMN ' . $this->dataColumnName()); 0092 $db->execute($stmt); 0093 } 0094 } catch (RESTException $e) { 0095 // don't fail hard on column removal, this can leave the db and our schema description in an inconsistent state 0096 // or block product deletion entirely 0097 error_log($e->getMessage()); 0098 // restart transaction so this doesn't block subsequent queries 0099 $db->rollBack(); 0100 $db->beginTransaction(); 0101 } 0102 } 0103 0104 /** Convert a JSON array into an array of SchemaEntryElement instances. */ 0105 static public function fromJson($jsonArray, SchemaEntry &$entry) 0106 { 0107 $elems = array(); 0108 foreach ($jsonArray as $jsonObj) { 0109 if (!property_exists($jsonObj, 'name') || !property_exists($jsonObj, 'type')) 0110 throw new RESTException('Incomplete schema entry element.', 400); 0111 $e = new SchemaEntryElement($entry); 0112 $e->name = $jsonObj->name; 0113 $e->type = $jsonObj->type; 0114 if (!$e->isValid()) 0115 throw new RESTException('Invalid schema entry element.', 400); 0116 array_push($elems, $e); 0117 } 0118 return $elems; 0119 } 0120 0121 0122 /** SQL type for this element. */ 0123 private function sqlType(Datastore $db) 0124 { 0125 switch ($this->type) { 0126 case self::STRING_TYPE: return Utils::sqlStringType($db->driver()); 0127 case self::INT_TYPE: return 'INTEGER'; 0128 case self::NUMBER_TYPE: return 'REAL'; 0129 case self::BOOL_TYPE: return 'BOOLEAN'; 0130 } 0131 throw new RESTException('Unsupported schema entry element type.', 400); 0132 } 0133 0134 /** Creates a data table entry for scalar elements. */ 0135 private function createScalarDataTableColumn(Datastore $db) 0136 { 0137 $stmt = $db->prepare('ALTER TABLE ' . $this->schemaEntry->product()->dataTableName() 0138 . ' ADD COLUMN ' . $this->dataColumnName() . ' ' . $this->sqlType($db)); 0139 $db->execute($stmt); 0140 } 0141 0142 /** Creates a data table entry for non-scalar elements. */ 0143 private function createNonScalarDataTableColumn(Datastore $db) 0144 { 0145 $stmt = $db->prepare('ALTER TABLE ' . $this->schemaEntry->dataTableName() 0146 . ' ADD COLUMN ' . $this->dataColumnName() . ' ' . $this->sqlType($db)); 0147 $db->execute($stmt); 0148 } 0149 } 0150 0151 ?>