Completed
Pull Request — master (#49)
by Thomas
03:23
created

Sqlite::insertAndSyncWithAutoInc()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 27
ccs 18
cts 18
cp 1
rs 9.488
c 0
b 0
f 0
cc 3
nc 3
nop 1
crap 3
1
<?php
2
3
namespace ORM\Dbal;
4
5
use ORM\Entity;
6
use ORM\Exception;
7
8
/**
9
 * Database abstraction for SQLite databases
10
 *
11
 * @package ORM\Dbal
12
 * @author  Thomas Flori <[email protected]>
13
 */
14
class Sqlite extends Dbal
15
{
16
    protected static $typeMapping = [
17
        'integer' => Type\Number::class,
18
        'int'     => Type\Number::class,
19
        'double'  => Type\Number::class,
20
        'real'    => Type\Number::class,
21
        'float'   => Type\Number::class,
22
        'numeric' => Type\Number::class,
23
        'decimal' => Type\Number::class,
24
25
        'varchar'   => Type\VarChar::class,
26
        'character' => Type\VarChar::class,
27
28
        'text' => Type\Text::class,
29
30
        'boolean' => Type\Boolean::class,
31
        'json'    => Type\Json::class,
32
33
        'datetime' => Type\DateTime::class,
34
        'date'     => Type\DateTime::class,
35
        'time'     => Type\Time::class,
36
    ];
37
38 1
    public function insertAndSyncWithAutoInc(Entity ...$entities)
39
    {
40 1
        if (count($entities) === 0) {
41
            return false;
42
        }
43 1
        static::assertSameType($entities);
44
45 1
        $entity = reset($entities);
46 1
        $pdo = $this->entityManager->getConnection();
47 1
        $table = $this->escapeIdentifier($entity::getTableName());
48 1
        $pKey = $this->escapeIdentifier($entity::getColumnName($entity::getPrimaryKeyVars()[0]));
49 1
        $pdo->beginTransaction();
50 1
        $pdo->query($this->buildInsertStatement(...$entities));
51 1
        $rows = $pdo->query('SELECT * FROM ' . $table . ' WHERE ' . $pKey . ' <= ' . $pdo->lastInsertId() .
52 1
                            ' ORDER BY ' . $pKey . ' DESC LIMIT ' . count($entities))
53 1
            ->fetchAll(\PDO::FETCH_ASSOC);
54 1
        $pdo->commit();
55
56
        /** @var Entity $entity */
57 1
        foreach (array_reverse($entities) as $key => $entity) {
58 1
            $entity->setOriginalData($rows[$key]);
59 1
            $entity->reset();
60 1
            $this->entityManager->map($entity, true);
61
        }
62
63 1
        return true;
64
    }
65
66 32
    public function describe($schemaTable)
67
    {
68 32
        $table = explode($this->identifierDivider, $schemaTable);
69 32
        list($schema, $table) = count($table) === 2 ? $table : [ null, $table[ 0 ] ];
70 32
        $schema = $schema !== null ? $this->escapeIdentifier($schema) . '.' : '';
71
72 32
        $result     = $this->entityManager->getConnection()->query(
73 32
            'PRAGMA ' . $schema . 'table_info(' . $this->escapeIdentifier($table) . ')'
74
        );
75 32
        $rawColumns = $result->fetchAll(\PDO::FETCH_ASSOC);
76
77 32
        if (count($rawColumns) === 0) {
78 2
            throw new Exception('Unknown table ' . $table);
79
        }
80
81 30
        $hasMultiplePrimaryKey = $this->hasMultiplePrimaryKey($rawColumns);
82
83 30
        $cols = array_map(function ($rawColumn) use ($hasMultiplePrimaryKey) {
84 30
            $columnDefinition = $this->normalizeColumnDefinition($rawColumn, $hasMultiplePrimaryKey);
85 30
            return new Column($this, $columnDefinition);
86 30
        }, $rawColumns);
87
88 30
        return new Table($cols);
89
    }
90
91
    /**
92
     * Checks $rawColumns for a multiple primary key
93
     *
94
     * @param array $rawColumns
95
     * @return bool
96
     */
97 30
    protected function hasMultiplePrimaryKey($rawColumns)
98
    {
99 30
        return count(array_filter(array_map(
100 30
            function ($rawColumn) {
101 30
                return $rawColumn[ 'pk' ];
102 30
            },
103
            $rawColumns
104 30
        ))) > 1;
105
    }
106
107
    /**
108
     * Normalize a column definition
109
     *
110
     * The column definition from "PRAGMA table_info(<table>)" is to special as useful. Here we normalize it to a more
111
     * ANSI-SQL style.
112
     *
113
     * @param array $rawColumn
114
     * @param bool  $hasMultiplePrimaryKey
115
     * @return array
116
     */
117 30
    protected function normalizeColumnDefinition($rawColumn, $hasMultiplePrimaryKey = false)
118
    {
119 30
        $definition = [];
120
121 30
        $definition['data_type'] = $this->normalizeType($rawColumn['type']);
122 30
        if (isset(static::$typeMapping[$definition['data_type']])) {
123 29
            $definition['type'] = static::$typeMapping[$definition['data_type']];
124
        }
125
126 30
        $definition['column_name']              = $rawColumn['name'];
127 30
        $definition['is_nullable']              = $rawColumn['notnull'] === '0';
128 30
        $definition['column_default']           = $rawColumn['dflt_value'];
129 30
        $definition['character_maximum_length'] = null;
130 30
        $definition['datetime_precision']       = null;
131
132 30
        switch ($definition['data_type']) {
133 30
            case 'varchar':
134 28
            case 'char':
135 2
                $definition['character_maximum_length'] = $this->extractParenthesis($rawColumn['type']);
136 2
                break;
137 28
            case 'datetime':
138 27
            case 'timestamp':
139 27
            case 'time':
140 2
                $definition['datetime_precision'] = $this->extractParenthesis($rawColumn['type']);
141 2
                break;
142 26
            case 'integer':
143 9
                if (!$definition['column_default'] && $rawColumn['pk'] === '1' && !$hasMultiplePrimaryKey) {
144 1
                    $definition['column_default'] = 'sequence(rowid)';
145
                }
146 9
                break;
147
        }
148
149 30
        return $definition;
150
    }
151
}
152