Passed
Branch master (056094)
by Andreas
04:47
created

subscriber::onSchemaCreateTable()   F

Complexity

Conditions 24
Paths 1479

Size

Total Lines 81
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 37
CRAP Score 59.0691

Importance

Changes 0
Metric Value
cc 24
eloc 45
c 0
b 0
f 0
nc 1479
nop 1
dl 0
loc 81
ccs 37
cts 61
cp 0.6066
crap 59.0691
rs 2.2275

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @author CONTENT CONTROL http://www.contentcontrol-berlin.de/
4
 * @copyright CONTENT CONTROL http://www.contentcontrol-berlin.de/
5
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License
6
 */
7
8
namespace midgard\portable\storage;
9
10
use midgard\portable\storage\interfaces\metadata;
11
use midgard\portable\api\dbobject;
12
use midgard\portable\api\repligard;
13
use midgard\portable\storage\type\datetime;
14
use Doctrine\Common\EventSubscriber;
15
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
16
use Doctrine\ORM\Events;
17
use Doctrine\ORM\EntityManagerInterface;
18
use Doctrine\ORM\Event\OnFlushEventArgs;
19
use Doctrine\DBAL\Schema\Column;
20
use Doctrine\DBAL\Types\Type;
21
use Doctrine\DBAL\Events as dbal_events;
22
use Doctrine\DBAL\Event\SchemaCreateTableEventArgs;
23
use Doctrine\DBAL\Event\SchemaColumnDefinitionEventArgs;
24
use Doctrine\ORM\Tools\ToolEvents;
25
use Doctrine\ORM\Tools\Event\GenerateSchemaTableEventArgs;
26
27
class subscriber implements EventSubscriber
28
{
29
    const ACTION_NONE = 0;
30
    const ACTION_DELETE = 1;
31
    const ACTION_PURGE = 2;
32
    const ACTION_CREATE = 3;
33
    const ACTION_UPDATE = 4;
34
35 116
    public function onFlush(OnFlushEventArgs $args)
36
    {
37 116
        $em = $args->getEntityManager();
38 116
        $uow = $em->getUnitOfWork();
39
40 116
        foreach ($uow->getScheduledEntityInsertions() as $entity) {
41 116
            $this->on_create($entity, $em);
42 116
        }
43
44 116
        foreach ($uow->getScheduledEntityUpdates() as $entity) {
45 40
            $this->on_update($entity, $em);
46 116
        }
47
48 116
        foreach ($uow->getScheduledEntityDeletions() as $entity) {
49 18
            $this->on_remove($entity, $em);
50 116
        }
51 116
    }
52
53 116
    private function on_create(dbobject $entity, EntityManagerInterface $em)
54
    {
55 116
        $cm = $em->getClassMetadata(get_class($entity));
56 116
        if (!($entity instanceof repligard)) {
57 116
            if (empty($entity->guid)) {
0 ignored issues
show
Bug Best Practice introduced by
The property $guid is declared protected in midgard\portable\api\dbobject. Since you implement __get, consider adding a @property or @property-read.
Loading history...
58 103
                $entity->set_guid(connection::generate_guid());
0 ignored issues
show
Bug introduced by
The method set_guid() does not exist on midgard\portable\api\dbobject. It seems like you code against a sub-type of midgard\portable\api\dbobject such as midgard\portable\api\mgdobject. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

58
                $entity->/** @scrutinizer ignore-call */ 
59
                         set_guid(connection::generate_guid());
Loading history...
59 103
                $em->getUnitOfWork()->recomputeSingleEntityChangeSet($cm, $entity);
60 103
            }
61
62 116
            $om = new objectmanager($em);
63 116
            $repligard_cm = $em->getClassMetadata('midgard:midgard_repligard');
64 116
            $repligard_entry = $om->new_instance($repligard_cm->getName());
65 116
            $repligard_entry->typename = $cm->getReflectionClass()->getShortName();
0 ignored issues
show
Bug Best Practice introduced by
The property typename does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
66 116
            $repligard_entry->guid = $entity->guid;
67 116
            $repligard_entry->object_action = self::ACTION_CREATE;
0 ignored issues
show
Bug Best Practice introduced by
The property object_action does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
68 116
            $em->persist($repligard_entry);
69 116
            $em->getUnitOfWork()->computeChangeSet($repligard_cm, $repligard_entry);
70 116
        }
71
72 116
        if ($entity instanceof metadata) {
73 98
            $entity->metadata->created = new \midgard_datetime();
0 ignored issues
show
Bug Best Practice introduced by
The property metadata does not exist on midgard\portable\api\dbobject. Since you implemented __get, consider adding a @property annotation.
Loading history...
74
            // we copy here instead of creating a new, because otherwise we might have
75
            // a one second difference if the code runs at the right millisecond
76 98
            $entity->metadata->revised = $entity->metadata->created;
77 98
            if ($user = connection::get_user()) {
78 7
                $entity->metadata_creator = $user->person;
0 ignored issues
show
Bug Best Practice introduced by
The property metadata_creator does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
79 7
                $entity->metadata_revisor = $user->person;
0 ignored issues
show
Bug Best Practice introduced by
The property metadata_revisor does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
80 7
            }
81 98
            $entity->metadata->size = $this->calculate_size($cm, $entity);
82 98
            $em->getUnitOfWork()->recomputeSingleEntityChangeSet($cm, $entity);
83 98
        }
84 116
    }
85
86 40
    private function on_update(dbobject $entity, EntityManagerInterface $em)
87
    {
88 40
        if ($entity instanceof metadata) {
89 39
            $cm = $em->getClassMetadata(get_class($entity));
90 39
            $entity->metadata_revised = new \midgard_datetime();
0 ignored issues
show
Bug Best Practice introduced by
The property metadata_revised does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
91 39
            $entity->metadata_revision++;
0 ignored issues
show
Bug Best Practice introduced by
The property metadata_revision does not exist on midgard\portable\api\dbobject. Since you implemented __get, consider adding a @property annotation.
Loading history...
92 39
            if ($user = connection::get_user()) {
93 7
                $entity->metadata_revisor = $user->person;
0 ignored issues
show
Bug Best Practice introduced by
The property metadata_revisor does not exist on midgard\portable\api\dbobject. Since you implemented __set, consider adding a @property annotation.
Loading history...
94 7
            }
95 39
            $entity->metadata->size = $this->calculate_size($cm, $entity);
0 ignored issues
show
Bug Best Practice introduced by
The property metadata does not exist on midgard\portable\api\dbobject. Since you implemented __get, consider adding a @property annotation.
Loading history...
96 39
            $em->getUnitOfWork()->recomputeSingleEntityChangeSet($cm, $entity);
97 39
        }
98
99 40
        if (!($entity instanceof repligard)) {
100 40
            $repligard_entry = $em->getRepository('midgard:midgard_repligard')->findOneBy(['guid' => $entity->guid]);
0 ignored issues
show
Bug Best Practice introduced by
The property $guid is declared protected in midgard\portable\api\dbobject. Since you implement __get, consider adding a @property or @property-read.
Loading history...
101
102
            if (   $entity instanceof metadata
103 40
                && $entity->{metadata::DELETED_FIELD}) {
104 27
                $repligard_entry->object_action = self::ACTION_DELETE;
105 27
            } else {
106 21
                $repligard_entry->object_action = self::ACTION_UPDATE;
107
            }
108 40
            $em->persist($repligard_entry);
109 40
            $em->getUnitOfWork()->computeChangeSet($em->getClassMetadata('midgard:midgard_repligard'), $repligard_entry);
110 40
        }
111 40
    }
112
113 18
    private function on_remove(dbobject $entity, EntityManagerInterface $em)
114
    {
115 18
        if (!($entity instanceof repligard)) {
116 18
            $repligard_entry = $em->getRepository('midgard:midgard_repligard')->findOneBy(['guid' => $entity->guid]);
0 ignored issues
show
Bug Best Practice introduced by
The property $guid is declared protected in midgard\portable\api\dbobject. Since you implement __get, consider adding a @property or @property-read.
Loading history...
117 18
            $repligard_entry->object_action = self::ACTION_PURGE;
118 18
            $em->persist($repligard_entry);
119 18
            $em->getUnitOfWork()->computeChangeSet($em->getClassMetadata('midgard:midgard_repligard'), $repligard_entry);
120 18
        }
121 18
    }
122
123 98
    private function calculate_size(ClassMetadata $cm, metadata $entity)
124
    {
125 98
        $size = 0;
126 98
        foreach ($cm->getAssociationNames() as $name) {
127 94
            $size += strlen($entity->$name);
128 98
        }
129 98
        foreach ($cm->getFieldNames() as $name) {
130 98
            $size += strlen($entity->$name);
131 98
        }
132 98
        return $size;
133
    }
134
135 7
    public function onSchemaCreateTable(SchemaCreateTableEventArgs $args)
136
    {
137 7
        $platform = $args->getPlatform();
138 7
        $columns = $args->getColumns();
139 7
        $modified = false;
140
141 7
        foreach ($columns as $name => &$config) {
142 7
            if ($platform->getName() === 'sqlite') {
143 7
                if (   !empty($config['primary'])
144 7
                    && !empty($config['autoincrement'])) {
145
                    /*
146
                     * This is essentially a workaround for http://www.doctrine-project.org/jira/browse/DBAL-642
147
                     * It makes sure we get auto increment behavior similar to msyql (i.e. IDs unique during table's lifetime)
148
                     */
149 5
                    $modified = true;
150 5
                    $config['columnDefinition'] = 'INTEGER PRIMARY KEY AUTOINCREMENT';
151 5
                }
152 7
                if (   !empty($config['comment'])
153 7
                    && $config['comment'] == 'BINARY') {
154 1
                    $modified = true;
155 1
                    $config['columnDefinition'] = $config['type']->getSQLDeclaration($config, $platform) . ' COLLATE BINARY' . $platform->getDefaultValueDeclarationSQL($config);
156 1
                }
157 7
            }
158 7
            if ($platform->getName() === 'mysql') {
159
                if (!empty($config['comment'])) {
160
                    if ($config['comment'] == 'BINARY') {
161
                        $modified = true;
162
                        $config['columnDefinition'] = $config['type']->getSQLDeclaration($config, $platform) . ' CHARACTER SET utf8 COLLATE utf8_bin' . $platform->getDefaultValueDeclarationSQL($config);
163
                    }
164
                    if (substr(strtolower(trim($config['comment'])), 0, 3) == 'set') {
165
                        $modified = true;
166
                        $config['columnDefinition'] = $config['comment'] . $platform->getDefaultValueDeclarationSQL($config);
167
                    }
168
                    if (!empty($config['columnDefinition']) && $platform->supportsInlineColumnComments()) {
169
                        $config['columnDefinition'] .=  " COMMENT " . $platform->quoteStringLiteral($config['comment']);
170
                    }
171
                }
172
            }
173 7
        }
174
175 7
        if (!$modified) {
176 4
            return;
177
        }
178
179 6
        $args->preventDefault();
180
181
        //The following is basically copied from the respective Doctrine function, since there seems to be no way
182
        //to just modify columns and pass them back to the SchemaManager
183 6
        $table = $args->getTable();
184 6
        $options = $args->getOptions();
185
186 6
        $queryFields = $platform->getColumnDeclarationListSQL($columns);
187
188 6
        if (!empty($options['uniqueConstraints'])) {
189
            foreach ($options['uniqueConstraints'] as $name => $definition) {
190
                $queryFields .= ', ' . $platform->getUniqueConstraintDeclarationSQL($name, $definition);
191
            }
192
        }
193
194 6
        if (!empty($options['foreignKeys'])) {
195
            foreach ($options['foreignKeys'] as $foreignKey) {
196
                $queryFields .= ', ' . $platform->getForeignKeyDeclarationSQL($foreignKey);
197
            }
198
        }
199
200 6
        $name = str_replace('.', '__', $table->getName());
201 6
        $args->addSql('CREATE TABLE ' . $name . ' (' . $queryFields . ')');
202
203 6
        if (isset($options['alter']) && true === $options['alter']) {
204 1
            return;
205
        }
206
207 6
        if (!empty($options['indexes'])) {
208 4
            foreach ($options['indexes'] as $indexDef) {
209 4
                $args->addSql($platform->getCreateIndexSQL($indexDef, $name));
210 4
            }
211 4
        }
212
213 6
        if (!empty($options['unique'])) {
214
            foreach ($options['unique'] as $indexDef) {
215
                $args->addSql($platform->getCreateIndexSQL($indexDef, $name));
216
            }
217
        }
218 6
    }
219
220
    /**
221
     * This function contains workarounds for reading existing Midgard databases
222
     *
223
     * ENUM fields are converted to string for now (Like in the XML reader)
224
     * DATETIME files use our custom datetime type
225
     */
226 2
    public function onSchemaColumnDefinition(SchemaColumnDefinitionEventArgs $args)
227
    {
228 2
        $column = array_change_key_case($args->getTableColumn(), CASE_LOWER);
229 2
        $type = strtok($column['type'], '()');
230
231 2
        if ($type == 'enum') {
232
            $args->preventDefault();
233
234
            $options = [
235
                'length' => 255,
236
                'default' => isset($column['default']) ? $column['default'] : null,
237
                'notnull' => (bool) ($column['null'] != 'YES'),
238
                'comment' => $column['type']
239
            ];
240
241
            $args->setColumn(new Column($column['field'], Type::getType(Type::STRING), $options));
242 2
        } elseif ($type == 'datetime') {
243
            $args->preventDefault();
244
            $options = [
245
                'default' => isset($column['default']) ? $column['default'] : null,
246
                'notnull' => (bool) ($column['null'] != 'YES'),
247
            ];
248
249
            $args->setColumn(new Column($column['field'], Type::getType(datetime::TYPE), $options));
250
        }
251 2
    }
252
253
    /**
254
     * This is mostly a workaround for the fact that SchemaTool wants to create FKs on
255
     * each run since it doesn't detect that MyISAM tables don't support them
256
     *
257
     * @see http://www.doctrine-project.org/jira/browse/DDC-3460
258
     * @param GenerateSchemaTableEventArgs $args
259
     */
260 4
    public function postGenerateSchemaTable(GenerateSchemaTableEventArgs $args)
261
    {
262 4
        $table = $args->getClassTable();
263 4
        if (   !$table->hasOption('engine')
264 4
            || $table->getOption('engine') !== 'MyISAM') {
265
            return;
266
        }
267 4
        foreach ($table->getForeignKeys() as $key) {
268 3
            $table->removeForeignKey($key->getName());
269 4
        }
270 4
    }
271
272 10
    public function getSubscribedEvents()
273
    {
274
        return [
275 10
            Events::onFlush,
276 10
            dbal_events::onSchemaCreateTable, dbal_events::onSchemaColumnDefinition,
277
            ToolEvents::postGenerateSchemaTable
278 10
        ];
279
    }
280
}
281