Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
      • Traits
    • Reflection
    • Security
    • Tokenizer
    • Utils
  • Tracy
    • Bridges
      • Nette
  • none

Classes

  • Connection
  • Context
  • Helpers
  • ResultSet
  • Row
  • SqlLiteral
  • SqlPreprocessor
  • Structure

Interfaces

  • IConventions
  • IRow
  • IRowContainer
  • IStructure
  • ISupplementalDriver

Exceptions

  • ConnectionException
  • ConstraintViolationException
  • DriverException
  • ForeignKeyConstraintViolationException
  • NotNullConstraintViolationException
  • UniqueConstraintViolationException
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
  6:  */
  7: 
  8: namespace Nette\Database;
  9: 
 10: use Nette;
 11: 
 12: 
 13: /**
 14:  * Cached reflection of database structure.
 15:  */
 16: class Structure implements IStructure
 17: {
 18:     use Nette\SmartObject;
 19: 
 20:     /** @var Connection */
 21:     protected $connection;
 22: 
 23:     /** @var Nette\Caching\Cache */
 24:     protected $cache;
 25: 
 26:     /** @var array */
 27:     protected $structure;
 28: 
 29:     /** @var bool */
 30:     protected $isRebuilt = false;
 31: 
 32: 
 33:     public function __construct(Connection $connection, Nette\Caching\IStorage $cacheStorage)
 34:     {
 35:         $this->connection = $connection;
 36:         $this->cache = new Nette\Caching\Cache($cacheStorage, 'Nette.Database.Structure.' . md5($this->connection->getDsn()));
 37:     }
 38: 
 39: 
 40:     public function getTables()
 41:     {
 42:         $this->needStructure();
 43:         return $this->structure['tables'];
 44:     }
 45: 
 46: 
 47:     public function getColumns($table)
 48:     {
 49:         $this->needStructure();
 50:         $table = $this->resolveFQTableName($table);
 51: 
 52:         return $this->structure['columns'][$table];
 53:     }
 54: 
 55: 
 56:     public function getPrimaryKey($table)
 57:     {
 58:         $this->needStructure();
 59:         $table = $this->resolveFQTableName($table);
 60: 
 61:         if (!isset($this->structure['primary'][$table])) {
 62:             return null;
 63:         }
 64: 
 65:         return $this->structure['primary'][$table];
 66:     }
 67: 
 68: 
 69:     public function getPrimaryAutoincrementKey($table)
 70:     {
 71:         $primaryKey = $this->getPrimaryKey($table);
 72:         if (!$primaryKey) {
 73:             return null;
 74:         }
 75: 
 76:         // Search for autoincrement key from multi primary key
 77:         if (is_array($primaryKey)) {
 78:             $keys = array_flip($primaryKey);
 79:             foreach ($this->getColumns($table) as $column) {
 80:                 if (isset($keys[$column['name']]) && $column['autoincrement']) {
 81:                     return $column['name'];
 82:                 }
 83:             }
 84:             return null;
 85:         }
 86: 
 87:         // Search for autoincrement key from simple primary key
 88:         foreach ($this->getColumns($table) as $column) {
 89:             if ($column['name'] == $primaryKey) {
 90:                 return $column['autoincrement'] ? $column['name'] : null;
 91:             }
 92:         }
 93: 
 94:         return null;
 95:     }
 96: 
 97: 
 98:     public function getPrimaryKeySequence($table)
 99:     {
100:         $this->needStructure();
101:         $table = $this->resolveFQTableName($table);
102: 
103:         if (!$this->connection->getSupplementalDriver()->isSupported(ISupplementalDriver::SUPPORT_SEQUENCE)) {
104:             return null;
105:         }
106: 
107:         $autoincrementPrimaryKeyName = $this->getPrimaryAutoincrementKey($table);
108:         if (!$autoincrementPrimaryKeyName) {
109:             return null;
110:         }
111: 
112:         // Search for sequence from simple primary key
113:         foreach ($this->structure['columns'][$table] as $columnMeta) {
114:             if ($columnMeta['name'] === $autoincrementPrimaryKeyName) {
115:                 return isset($columnMeta['vendor']['sequence']) ? $columnMeta['vendor']['sequence'] : null;
116:             }
117:         }
118: 
119:         return null;
120:     }
121: 
122: 
123:     public function getHasManyReference($table, $targetTable = null)
124:     {
125:         $this->needStructure();
126:         $table = $this->resolveFQTableName($table);
127: 
128:         if ($targetTable) {
129:             $targetTable = $this->resolveFQTableName($targetTable);
130:             foreach ($this->structure['hasMany'][$table] as $key => $value) {
131:                 if (strtolower($key) === $targetTable) {
132:                     return $this->structure['hasMany'][$table][$key];
133:                 }
134:             }
135: 
136:             return null;
137: 
138:         } else {
139:             if (!isset($this->structure['hasMany'][$table])) {
140:                 return [];
141:             }
142:             return $this->structure['hasMany'][$table];
143:         }
144:     }
145: 
146: 
147:     public function getBelongsToReference($table, $column = null)
148:     {
149:         $this->needStructure();
150:         $table = $this->resolveFQTableName($table);
151: 
152:         if ($column) {
153:             $column = strtolower($column);
154:             if (!isset($this->structure['belongsTo'][$table][$column])) {
155:                 return null;
156:             }
157:             return $this->structure['belongsTo'][$table][$column];
158: 
159:         } else {
160:             if (!isset($this->structure['belongsTo'][$table])) {
161:                 return [];
162:             }
163:             return $this->structure['belongsTo'][$table];
164:         }
165:     }
166: 
167: 
168:     public function rebuild()
169:     {
170:         $this->structure = $this->loadStructure();
171:         $this->cache->save('structure', $this->structure);
172:     }
173: 
174: 
175:     public function isRebuilt()
176:     {
177:         return $this->isRebuilt;
178:     }
179: 
180: 
181:     protected function needStructure()
182:     {
183:         if ($this->structure !== null) {
184:             return;
185:         }
186: 
187:         $this->structure = $this->cache->load('structure', [$this, 'loadStructure']);
188:     }
189: 
190: 
191:     /**
192:      * @internal
193:      */
194:     public function loadStructure()
195:     {
196:         $driver = $this->connection->getSupplementalDriver();
197: 
198:         $structure = [];
199:         $structure['tables'] = $driver->getTables();
200: 
201:         foreach ($structure['tables'] as $tablePair) {
202:             if (isset($tablePair['fullName'])) {
203:                 $table = $tablePair['fullName'];
204:                 $structure['aliases'][strtolower($tablePair['name'])] = strtolower($table);
205:             } else {
206:                 $table = $tablePair['name'];
207:             }
208: 
209:             $structure['columns'][strtolower($table)] = $columns = $driver->getColumns($table);
210: 
211:             if (!$tablePair['view']) {
212:                 $structure['primary'][strtolower($table)] = $this->analyzePrimaryKey($columns);
213:                 $this->analyzeForeignKeys($structure, $table);
214:             }
215:         }
216: 
217:         if (isset($structure['hasMany'])) {
218:             foreach ($structure['hasMany'] as &$table) {
219:                 uksort($table, function ($a, $b) {
220:                     return strlen($a) - strlen($b);
221:                 });
222:             }
223:         }
224: 
225:         $this->isRebuilt = true;
226: 
227:         return $structure;
228:     }
229: 
230: 
231:     protected function analyzePrimaryKey(array $columns)
232:     {
233:         $primary = [];
234:         foreach ($columns as $column) {
235:             if ($column['primary']) {
236:                 $primary[] = $column['name'];
237:             }
238:         }
239: 
240:         if (count($primary) === 0) {
241:             return null;
242:         } elseif (count($primary) === 1) {
243:             return reset($primary);
244:         } else {
245:             return $primary;
246:         }
247:     }
248: 
249: 
250:     protected function analyzeForeignKeys(&$structure, $table)
251:     {
252:         $lowerTable = strtolower($table);
253:         foreach ($this->connection->getSupplementalDriver()->getForeignKeys($table) as $row) {
254:             $structure['belongsTo'][$lowerTable][$row['local']] = $row['table'];
255:             $structure['hasMany'][strtolower($row['table'])][$table][] = $row['local'];
256:         }
257: 
258:         if (isset($structure['belongsTo'][$lowerTable])) {
259:             uksort($structure['belongsTo'][$lowerTable], function ($a, $b) {
260:                 return strlen($a) - strlen($b);
261:             });
262:         }
263:     }
264: 
265: 
266:     protected function resolveFQTableName($table)
267:     {
268:         $name = strtolower($table);
269:         if (isset($this->structure['columns'][$name])) {
270:             return $name;
271:         }
272: 
273:         if (isset($this->structure['aliases'][$name])) {
274:             return $this->structure['aliases'][$name];
275:         }
276: 
277:         if (!$this->isRebuilt()) {
278:             $this->rebuild();
279:             return $this->resolveFQTableName($table);
280:         }
281: 
282:         throw new Nette\InvalidArgumentException("Table '$name' does not exist.");
283:     }
284: }
285: 
Nette 2.4-20180918 API API documentation generated by ApiGen 2.8.0