1 <?php
  2 
  3 /**
  4  * Class for generating the php code for a rex_sql_table definition.
  5  *
  6  * Especially useful to generate the code for the `install.php` of packages.
  7  *
  8  * @author gharlan
  9  *
 10  * @package redaxo\core\sql
 11  */
 12 class rex_sql_schema_dumper
 13 {
 14     /**
 15      * Dumps the schema for the given table as php code (using `rex_sql_table`).
 16      *
 17      * @param rex_sql_table $table
 18      *
 19      * @return string
 20      */
 21     public function dumpTable(rex_sql_table $table)
 22     {
 23         $code = 'rex_sql_table::get('.$this->tableName($table->getName()).')';
 24 
 25         $setPrimaryKey = true;
 26         $primaryKeyIsId = ['id'] === $table->getPrimaryKey();
 27         $idColumn = new rex_sql_column('id', 'int(10) unsigned', false, null, 'auto_increment');
 28 
 29         foreach ($table->getColumns() as $column) {
 30             if ($primaryKeyIsId && $column->equals($idColumn)) {
 31                 $code .= "\n    ->ensurePrimaryIdColumn()";
 32                 $setPrimaryKey = false;
 33 
 34                 continue;
 35             }
 36 
 37             $code .= "\n    ->ensureColumn(".$this->getColumn($column).')';
 38         }
 39 
 40         if ($setPrimaryKey && $table->getPrimaryKey()) {
 41             $code .= "\n    ->setPrimaryKey(".$this->getPrimaryKey($table->getPrimaryKey()).')';
 42         }
 43 
 44         foreach ($table->getIndexes() as $index) {
 45             $code .= "\n    ->ensureIndex(".$this->getIndex($index).')';
 46         }
 47 
 48         foreach ($table->getForeignKeys() as $foreignKey) {
 49             $code .= "\n    ->ensureForeignKey(".$this->getForeignKey($foreignKey).')';
 50         }
 51 
 52         $code .= "\n    ->ensure();\n";
 53 
 54         return $code;
 55     }
 56 
 57     private function getColumn(rex_sql_column $column)
 58     {
 59         $parameters = [];
 60         $nonDefault = false;
 61 
 62         if (null !== $column->getExtra()) {
 63             $parameters[] = $this->scalar($column->getExtra());
 64             $nonDefault = true;
 65         }
 66 
 67         if ($nonDefault || null !== $column->getDefault()) {
 68             $parameters[] = $this->scalar($column->getDefault());
 69             $nonDefault = true;
 70         }
 71 
 72         if ($nonDefault || false !== $column->isNullable()) {
 73             $parameters[] = $this->scalar($column->isNullable());
 74         }
 75 
 76         $parameters[] = $this->scalar($column->getType());
 77         $parameters[] = $this->scalar($column->getName());
 78 
 79         return 'new rex_sql_column('.implode(', ', array_reverse($parameters)).')';
 80     }
 81 
 82     private function getIndex(rex_sql_index $index)
 83     {
 84         $parameters = [
 85             $this->scalar($index->getName()),
 86             $this->simpleArray($index->getColumns()),
 87         ];
 88 
 89         static $types = [
 90             rex_sql_index::UNIQUE => 'rex_sql_index::UNIQUE',
 91             rex_sql_index::FULLTEXT => 'rex_sql_index::FULLTEXT',
 92         ];
 93 
 94         if (rex_sql_index::INDEX !== $index->getType()) {
 95             $parameters[] = $types[$index->getType()];
 96         }
 97 
 98         return 'new rex_sql_index('.implode(', ', $parameters).')';
 99     }
100 
101     private function getForeignKey(rex_sql_foreign_key $foreignKey)
102     {
103         $parameters = [
104             $this->scalar($foreignKey->getName()),
105             $this->tableName($foreignKey->getTable()),
106             $this->map($foreignKey->getColumns()),
107         ];
108 
109         static $options = [
110             rex_sql_foreign_key::RESTRICT => 'rex_sql_foreign_key::RESTRICT',
111             rex_sql_foreign_key::CASCADE => 'rex_sql_foreign_key::CASCADE',
112             rex_sql_foreign_key::SET_NULL => 'rex_sql_foreign_key::SET_NULL',
113         ];
114 
115         $nonDefaultOnDelete = rex_sql_foreign_key::RESTRICT !== $foreignKey->getOnDelete();
116 
117         if ($nonDefaultOnDelete || rex_sql_foreign_key::RESTRICT !== $foreignKey->getOnUpdate()) {
118             $parameters[] = $options[$foreignKey->getOnUpdate()];
119         }
120 
121         if ($nonDefaultOnDelete) {
122             $parameters[] = $options[$foreignKey->getOnDelete()];
123         }
124 
125         return 'new rex_sql_foreign_key('.implode(', ', $parameters).')';
126     }
127 
128     private function getPrimaryKey(array $primaryKey)
129     {
130         if (1 === count($primaryKey)) {
131             return $this->scalar(reset($primaryKey));
132         }
133 
134         return $this->simpleArray($primaryKey);
135     }
136 
137     private function tableName($name)
138     {
139         if (0 !== strpos($name, rex::getTablePrefix())) {
140             return $this->scalar($name);
141         }
142 
143         $name = substr($name, strlen(rex::getTablePrefix()));
144 
145         return 'rex::getTable('.$this->scalar($name).')';
146     }
147 
148     private function scalar($scalar)
149     {
150         if (null === $scalar) {
151             return 'null';
152         }
153 
154         return var_export($scalar, true);
155     }
156 
157     private function simpleArray(array $list)
158     {
159         $parts = [];
160 
161         foreach ($list as $value) {
162             $parts[] = $this->scalar($value);
163         }
164 
165         return '['.implode(', ', $parts).']';
166     }
167 
168     private function map(array $map)
169     {
170         $parts = [];
171 
172         foreach ($map as $key => $value) {
173             $parts[] = $this->scalar($key).' => '.$this->scalar($value);
174         }
175 
176         return '['.implode(', ', $parts).']';
177     }
178 }
179