Completed
Push — 1.0 ( db36e4...c33c9d )
by David
8s
created

ZohoDatabaseCopier::getTableName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 7
rs 9.4285
cc 1
eloc 4
nc 1
nop 1
1
<?php
2
3
namespace Wabel\Zoho\CRM\Copy;
4
5
use Doctrine\DBAL\Connection;
6
use Doctrine\DBAL\Schema\Schema;
7
use Doctrine\DBAL\Schema\SchemaDiff;
8
use Wabel\Zoho\CRM\AbstractZohoDao;
9
use function Stringy\create as s;
10
11
/**
12
 * This class is in charge of synchronizing one table of your database with Zoho records.
13
 */
14
class ZohoDatabaseCopier
15
{
16
    /**
17
     * @var Connection
18
     */
19
    private $connection;
20
21
    private $prefix;
22
23
    /**
24
     * @var ZohoChangeListener[]
25
     */
26
    private $listeners;
27
28
    /**
29
     * ZohoDatabaseCopier constructor.
30
     *
31
     * @param Connection $connection
32
     */
33
    public function __construct(Connection $connection, $prefix = 'zoho_', array $listeners = [])
34
    {
35
        $this->connection = $connection;
36
        $this->prefix = $prefix;
37
        $this->listeners = $listeners;
38
    }
39
40
    /**
41
     * @param AbstractZohoDao $dao
42
     * @param bool            $incrementalSync Whether we synchronize only the modified files or everything.
43
     */
44
    public function copy(AbstractZohoDao $dao, $incrementalSync = true)
45
    {
46
        $this->synchronizeDbModel($dao);
47
        $this->copyData($dao, $incrementalSync);
48
    }
49
50
    /**
51
     * Synchronizes the DB model with Zoho.
52
     *
53
     * @param AbstractZohoDao $dao
54
     *
55
     * @throws \Doctrine\DBAL\DBALException
56
     * @throws \Doctrine\DBAL\Schema\SchemaException
57
     */
58
    private function synchronizeDbModel(AbstractZohoDao $dao)
59
    {
60
        $tableName = $this->getTableName($dao);
61
62
        $schema = new Schema();
63
        $table = $schema->createTable($tableName);
64
65
        $flatFields = $this->getFlatFields($dao->getFields());
0 ignored issues
show
Bug introduced by
The method getFields() cannot be called from this context as it is declared protected in class Wabel\Zoho\CRM\AbstractZohoDao.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
66
67
        $table->addColumn('id', 'string', ['length' => 100]);
68
        $table->setPrimaryKey(['id']);
69
70
        foreach ($flatFields as $field) {
71
            $columnName = $field['name'];
72
73
            $length = null;
74
            $index = false;
75
76
            // Note: full list of types available here: https://www.zoho.com/crm/help/customization/custom-fields.html
77
            switch ($field['type']) {
78
                case 'Lookup ID':
79
                case 'Lookup':
80
                    $type = 'string';
81
                    $length = 100;
82
                    $index = true;
83
                    break;
84
                case 'OwnerLookup':
85
                    $type = 'string';
86
                    $index = true;
87
                    $length = 25;
88
                    break;
89
                case 'Formula':
90
                    // Note: a Formula can return any type, but we have no way to know which type it returns...
91
                    $type = 'string';
92
                    $length = 100;
93
                    break;
94
                case 'DateTime':
95
                    $type = 'datetime';
96
                    break;
97
                case 'Date':
98
                    $type = 'date';
99
                    break;
100
                case 'DateTime':
101
                    $type = 'datetime';
102
                    break;
103
                case 'Boolean':
104
                    $type = 'boolean';
105
                    break;
106
                case 'TextArea':
107
                    $type = 'text';
108
                    break;
109
                case 'BigInt':
110
                    $type = 'bigint';
111
                    break;
112
                case 'Phone':
113
                case 'Auto Number':
114
                case 'Text':
115
                case 'URL':
116
                case 'Email':
117
                case 'Website':
118
                case 'Pick List':
119
                case 'Multiselect Pick List':
120
                    $type = 'string';
121
                    $length = $field['maxlength'];
122
                    break;
123
                case 'Double':
124
                case 'Percent':
125
                    $type = 'float';
126
                    break;
127
                case 'Integer':
128
                    $type = 'integer';
129
                    break;
130
                case 'Currency':
131
                case 'Decimal':
132
                    $type = 'decimal';
133
                    break;
134
                default:
135
                    throw new \RuntimeException('Unknown type "'.$field['type'].'"');
136
            }
137
138
            $options = [];
139
140
            if ($length) {
141
                $options['length'] = $length;
142
            }
143
144
            //$options['notnull'] = $field['req'];
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
145
            $options['notnull'] = false;
146
147
            $table->addColumn($columnName, $type, $options);
148
149
            if ($index) {
150
                $table->addIndex([$columnName]);
151
            }
152
        }
153
154
        $dbSchema = $this->connection->getSchemaManager()->createSchema();
155
        if ($this->connection->getSchemaManager()->tablesExist($tableName)) {
0 ignored issues
show
Documentation introduced by
$tableName is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
156
            $dbTable = $dbSchema->getTable($tableName);
157
158
            $comparator = new \Doctrine\DBAL\Schema\Comparator();
159
            $tableDiff = $comparator->diffTable($dbTable, $table);
160
161 View Code Duplication
            if ($tableDiff !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
162
                $diff = new SchemaDiff();
163
                $diff->fromSchema = $dbSchema;
164
                $diff->changedTables[$tableName] = $tableDiff;
165
                $statements = $diff->toSaveSql($this->connection->getDatabasePlatform());
166
                foreach ($statements as $sql) {
167
                    $this->connection->exec($sql);
168
                }
169
            }
170 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
171
            $diff = new SchemaDiff();
172
            $diff->fromSchema = $dbSchema;
173
            $diff->newTables[$tableName] = $table;
174
            $statements = $diff->toSaveSql($this->connection->getDatabasePlatform());
175
            foreach ($statements as $sql) {
176
                $this->connection->exec($sql);
177
            }
178
        }
179
    }
180
181
    /**
182
     * @param AbstractZohoDao $dao
183
     * @param bool            $incrementalSync Whether we synchronize only the modified files or everything.
184
     *
185
     * @throws \Doctrine\DBAL\DBALException
186
     * @throws \Doctrine\DBAL\Schema\SchemaException
187
     * @throws \Wabel\Zoho\CRM\Exception\ZohoCRMResponseException
188
     */
189
    private function copyData(AbstractZohoDao $dao, $incrementalSync = true)
190
    {
191
        $tableName = $this->getTableName($dao);
192
193
        if ($incrementalSync) {
194
            // Let's get the last modification date:
195
            $lastActivityTime = $this->connection->fetchColumn('SELECT MAX(lastActivityTime) FROM '.$tableName);
196
            if ($lastActivityTime !== null) {
197
                $lastActivityTime = new \DateTime($lastActivityTime);
198
            }
199
            $records = $dao->getRecords(null, null, $lastActivityTime);
200
        } else {
201
            $records = $dao->getRecords();
202
        }
203
204
        $table = $this->connection->getSchemaManager()->createSchema()->getTable($tableName);
205
206
        $flatFields = $this->getFlatFields($dao->getFields());
0 ignored issues
show
Bug introduced by
The method getFields() cannot be called from this context as it is declared protected in class Wabel\Zoho\CRM\AbstractZohoDao.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
207
        $fieldsByName = [];
208
        foreach ($flatFields as $field) {
209
            $fieldsByName[$field['name']] = $field;
210
        }
211
212
        $select = $this->connection->prepare('SELECT * FROM '.$tableName.' WHERE id = :id');
213
214
        $this->connection->beginTransaction();
215
216
        foreach ($records as $record) {
217
            $data = [];
218
            $types = [];
219
            foreach ($table->getColumns() as $column) {
220
                if ($column->getName() === 'id') {
221
                    continue;
222
                } else {
223
                    $field = $fieldsByName[$column->getName()];
224
                    $getterName = $field['getter'];
225
                    $data[$column->getName()] = $record->$getterName();
226
                    $types[$column->getName()] = $column->getType()->getName();
227
                }
228
            }
229
230
            $select->execute(['id' => $record->getZohoId()]);
231
            $result = $select->fetch(\PDO::FETCH_ASSOC);
232
            if ($result === false) {
233
                $data['id'] = $record->getZohoId();
234
                $types['id'] = 'string';
235
236
                $this->connection->insert($tableName, $data, $types);
237
238
                foreach ($this->listeners as $listener) {
239
                    $listener->onInsert($data, $dao);
240
                }
241
            } else {
242
                $identifier = ['id' => $record->getZohoId()];
243
                $types['id'] = 'string';
244
245
                $this->connection->update($tableName, $data, $identifier, $types);
246
247
                // Let's add the id for the update trigger
248
                $data['id'] = $record->getZohoId();
249
                foreach ($this->listeners as $listener) {
250
                    $listener->onUpdate($data, $result, $dao);
251
                }
252
            }
253
        }
254
255
        $this->connection->commit();
256
    }
257
258
    private function getFlatFields(array $fields)
259
    {
260
        $flatFields = [];
261
        foreach ($fields as $cat) {
262
            $flatFields = array_merge($flatFields, $cat);
263
        }
264
265
        return $flatFields;
266
    }
267
268
    /**
269
     * Computes the name of the table based on the DAO plural module name.
270
     *
271
     * @param AbstractZohoDao $dao
272
     *
273
     * @return string
274
     */
275
    private function getTableName(AbstractZohoDao $dao)
276
    {
277
        $tableName = $this->prefix.$dao->getPluralModuleName();
0 ignored issues
show
Bug introduced by
The method getPluralModuleName() cannot be called from this context as it is declared protected in class Wabel\Zoho\CRM\AbstractZohoDao.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
278
        $tableName = s($tableName)->upperCamelize()->underscored();
279
280
        return (string) $tableName;
281
    }
282
}
283