|
1 <?php |
|
2 /* |
|
3 * $Id$ |
|
4 * |
|
5 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
6 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
7 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
8 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
9 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
10 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
11 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
12 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
13 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
14 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
15 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
16 * |
|
17 * This software consists of voluntary contributions made by many individuals |
|
18 * and is licensed under the LGPL. For more information, see |
|
19 * <http://www.doctrine-project.org>. |
|
20 */ |
|
21 |
|
22 namespace Doctrine\ORM\Tools; |
|
23 |
|
24 use Doctrine\ORM\Mapping\ClassMetadataInfo, |
|
25 Doctrine\ORM\Tools\Export\Driver\AbstractExporter, |
|
26 Doctrine\Common\Util\Inflector; |
|
27 |
|
28 /** |
|
29 * Class to help with converting Doctrine 1 schema files to Doctrine 2 mapping files |
|
30 * |
|
31 * @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
|
32 * @link www.doctrine-project.org |
|
33 * @since 2.0 |
|
34 * @version $Revision$ |
|
35 * @author Guilherme Blanco <guilhermeblanco@hotmail.com> |
|
36 * @author Jonathan Wage <jonwage@gmail.com> |
|
37 * @author Roman Borschel <roman@code-factory.org> |
|
38 */ |
|
39 class ConvertDoctrine1Schema |
|
40 { |
|
41 private $_legacyTypeMap = array( |
|
42 // TODO: This list may need to be updated |
|
43 'clob' => 'text', |
|
44 'timestamp' => 'datetime', |
|
45 'enum' => 'string' |
|
46 ); |
|
47 |
|
48 /** |
|
49 * Constructor passes the directory or array of directories |
|
50 * to convert the Doctrine 1 schema files from |
|
51 * |
|
52 * @param array $from |
|
53 * @author Jonathan Wage |
|
54 */ |
|
55 public function __construct($from) |
|
56 { |
|
57 $this->_from = (array) $from; |
|
58 } |
|
59 |
|
60 /** |
|
61 * Get an array of ClassMetadataInfo instances from the passed |
|
62 * Doctrine 1 schema |
|
63 * |
|
64 * @return array $metadatas An array of ClassMetadataInfo instances |
|
65 */ |
|
66 public function getMetadata() |
|
67 { |
|
68 $schema = array(); |
|
69 foreach ($this->_from as $path) { |
|
70 if (is_dir($path)) { |
|
71 $files = glob($path . '/*.yml'); |
|
72 foreach ($files as $file) { |
|
73 $schema = array_merge($schema, (array) \Symfony\Component\Yaml\Yaml::parse($file)); |
|
74 } |
|
75 } else { |
|
76 $schema = array_merge($schema, (array) \Symfony\Component\Yaml\Yaml::parse($path)); |
|
77 } |
|
78 } |
|
79 |
|
80 $metadatas = array(); |
|
81 foreach ($schema as $className => $mappingInformation) { |
|
82 $metadatas[] = $this->_convertToClassMetadataInfo($className, $mappingInformation); |
|
83 } |
|
84 |
|
85 return $metadatas; |
|
86 } |
|
87 |
|
88 private function _convertToClassMetadataInfo($className, $mappingInformation) |
|
89 { |
|
90 $metadata = new ClassMetadataInfo($className); |
|
91 |
|
92 $this->_convertTableName($className, $mappingInformation, $metadata); |
|
93 $this->_convertColumns($className, $mappingInformation, $metadata); |
|
94 $this->_convertIndexes($className, $mappingInformation, $metadata); |
|
95 $this->_convertRelations($className, $mappingInformation, $metadata); |
|
96 |
|
97 return $metadata; |
|
98 } |
|
99 |
|
100 private function _convertTableName($className, array $model, ClassMetadataInfo $metadata) |
|
101 { |
|
102 if (isset($model['tableName']) && $model['tableName']) { |
|
103 $e = explode('.', $model['tableName']); |
|
104 if (count($e) > 1) { |
|
105 $metadata->table['schema'] = $e[0]; |
|
106 $metadata->table['name'] = $e[1]; |
|
107 } else { |
|
108 $metadata->table['name'] = $e[0]; |
|
109 } |
|
110 } |
|
111 } |
|
112 |
|
113 private function _convertColumns($className, array $model, ClassMetadataInfo $metadata) |
|
114 { |
|
115 $id = false; |
|
116 |
|
117 if (isset($model['columns']) && $model['columns']) { |
|
118 foreach ($model['columns'] as $name => $column) { |
|
119 $fieldMapping = $this->_convertColumn($className, $name, $column, $metadata); |
|
120 |
|
121 if (isset($fieldMapping['id']) && $fieldMapping['id']) { |
|
122 $id = true; |
|
123 } |
|
124 } |
|
125 } |
|
126 |
|
127 if ( ! $id) { |
|
128 $fieldMapping = array( |
|
129 'fieldName' => 'id', |
|
130 'columnName' => 'id', |
|
131 'type' => 'integer', |
|
132 'id' => true |
|
133 ); |
|
134 $metadata->mapField($fieldMapping); |
|
135 $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); |
|
136 } |
|
137 } |
|
138 |
|
139 private function _convertColumn($className, $name, $column, ClassMetadataInfo $metadata) |
|
140 { |
|
141 if (is_string($column)) { |
|
142 $string = $column; |
|
143 $column = array(); |
|
144 $column['type'] = $string; |
|
145 } |
|
146 if ( ! isset($column['name'])) { |
|
147 $column['name'] = $name; |
|
148 } |
|
149 // check if a column alias was used (column_name as field_name) |
|
150 if (preg_match("/(\w+)\sas\s(\w+)/i", $column['name'], $matches)) { |
|
151 $name = $matches[1]; |
|
152 $column['name'] = $name; |
|
153 $column['alias'] = $matches[2]; |
|
154 } |
|
155 if (preg_match("/([a-zA-Z]+)\(([0-9]+)\)/", $column['type'], $matches)) { |
|
156 $column['type'] = $matches[1]; |
|
157 $column['length'] = $matches[2]; |
|
158 } |
|
159 $column['type'] = strtolower($column['type']); |
|
160 // check if legacy column type (1.x) needs to be mapped to a 2.0 one |
|
161 if (isset($this->_legacyTypeMap[$column['type']])) { |
|
162 $column['type'] = $this->_legacyTypeMap[$column['type']]; |
|
163 } |
|
164 if ( ! \Doctrine\DBAL\Types\Type::hasType($column['type'])) { |
|
165 throw ToolsException::couldNotMapDoctrine1Type($column['type']); |
|
166 } |
|
167 |
|
168 $fieldMapping = array(); |
|
169 if (isset($column['primary'])) { |
|
170 $fieldMapping['id'] = true; |
|
171 } |
|
172 $fieldMapping['fieldName'] = isset($column['alias']) ? $column['alias'] : $name; |
|
173 $fieldMapping['columnName'] = $column['name']; |
|
174 $fieldMapping['type'] = $column['type']; |
|
175 if (isset($column['length'])) { |
|
176 $fieldMapping['length'] = $column['length']; |
|
177 } |
|
178 $allowed = array('precision', 'scale', 'unique', 'options', 'notnull', 'version'); |
|
179 foreach ($column as $key => $value) { |
|
180 if (in_array($key, $allowed)) { |
|
181 $fieldMapping[$key] = $value; |
|
182 } |
|
183 } |
|
184 |
|
185 $metadata->mapField($fieldMapping); |
|
186 |
|
187 if (isset($column['autoincrement'])) { |
|
188 $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_AUTO); |
|
189 } else if (isset($column['sequence'])) { |
|
190 $metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_SEQUENCE); |
|
191 $definition = array( |
|
192 'sequenceName' => is_array($column['sequence']) ? $column['sequence']['name']:$column['sequence'] |
|
193 ); |
|
194 if (isset($column['sequence']['size'])) { |
|
195 $definition['allocationSize'] = $column['sequence']['size']; |
|
196 } |
|
197 if (isset($column['sequence']['value'])) { |
|
198 $definition['initialValue'] = $column['sequence']['value']; |
|
199 } |
|
200 $metadata->setSequenceGeneratorDefinition($definition); |
|
201 } |
|
202 return $fieldMapping; |
|
203 } |
|
204 |
|
205 private function _convertIndexes($className, array $model, ClassMetadataInfo $metadata) |
|
206 { |
|
207 if (isset($model['indexes']) && $model['indexes']) { |
|
208 foreach ($model['indexes'] as $name => $index) { |
|
209 $type = (isset($index['type']) && $index['type'] == 'unique') |
|
210 ? 'uniqueConstraints' : 'indexes'; |
|
211 |
|
212 $metadata->table[$type][$name] = array( |
|
213 'columns' => $index['fields'] |
|
214 ); |
|
215 } |
|
216 } |
|
217 } |
|
218 |
|
219 private function _convertRelations($className, array $model, ClassMetadataInfo $metadata) |
|
220 { |
|
221 if (isset($model['relations']) && $model['relations']) { |
|
222 foreach ($model['relations'] as $name => $relation) { |
|
223 if ( ! isset($relation['alias'])) { |
|
224 $relation['alias'] = $name; |
|
225 } |
|
226 if ( ! isset($relation['class'])) { |
|
227 $relation['class'] = $name; |
|
228 } |
|
229 if ( ! isset($relation['local'])) { |
|
230 $relation['local'] = Inflector::tableize($relation['class']); |
|
231 } |
|
232 if ( ! isset($relation['foreign'])) { |
|
233 $relation['foreign'] = 'id'; |
|
234 } |
|
235 if ( ! isset($relation['foreignAlias'])) { |
|
236 $relation['foreignAlias'] = $className; |
|
237 } |
|
238 |
|
239 if (isset($relation['refClass'])) { |
|
240 $type = 'many'; |
|
241 $foreignType = 'many'; |
|
242 $joinColumns = array(); |
|
243 } else { |
|
244 $type = isset($relation['type']) ? $relation['type'] : 'one'; |
|
245 $foreignType = isset($relation['foreignType']) ? $relation['foreignType'] : 'many'; |
|
246 $joinColumns = array( |
|
247 array( |
|
248 'name' => $relation['local'], |
|
249 'referencedColumnName' => $relation['foreign'], |
|
250 'onDelete' => isset($relation['onDelete']) ? $relation['onDelete'] : null, |
|
251 'onUpdate' => isset($relation['onUpdate']) ? $relation['onUpdate'] : null, |
|
252 ) |
|
253 ); |
|
254 } |
|
255 |
|
256 if ($type == 'one' && $foreignType == 'one') { |
|
257 $method = 'mapOneToOne'; |
|
258 } else if ($type == 'many' && $foreignType == 'many') { |
|
259 $method = 'mapManyToMany'; |
|
260 } else { |
|
261 $method = 'mapOneToMany'; |
|
262 } |
|
263 |
|
264 $associationMapping = array(); |
|
265 $associationMapping['fieldName'] = $relation['alias']; |
|
266 $associationMapping['targetEntity'] = $relation['class']; |
|
267 $associationMapping['mappedBy'] = $relation['foreignAlias']; |
|
268 $associationMapping['joinColumns'] = $joinColumns; |
|
269 |
|
270 $metadata->$method($associationMapping); |
|
271 } |
|
272 } |
|
273 } |
|
274 } |