Passed
Pull Request — master (#20)
by Aleksei
01:42
created

FieldComparator::compare()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 6
c 1
b 0
f 0
nc 3
nop 0
dl 0
loc 10
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cycle\Schema\Definition\Comparator;
6
7
use Cycle\Schema\Definition\Field;
8
use Exception;
9
use InvalidArgumentException;
10
11
final class FieldComparator
12
{
13
    private $columnName;
14
    /** @var Field[] */
15
    private $fields = [];
16
17
    public function addField(string $key, Field $field): self
18
    {
19
        if ($this->columnName === null) {
20
            $this->columnName = $field->getColumn();
21
        }
22
        if ($this->columnName !== $field->getColumn()) {
23
            throw new InvalidArgumentException('The field comparator only accepts fields with the same column name.');
24
        }
25
        $this->fields[$key] = $field;
26
        return $this;
27
    }
28
29
    public function compare(): void
30
    {
31
        if (count($this->fields) <= 1) {
32
            return;
33
        }
34
        // Check options
35
        if (!$this->compareOptions() || !$this->compareProperties()) {
36
            throw new Exception(
37
                "Different definitions are specified for the `$this->columnName` column:"
38
                . "\n\n{$this->generateErrorText()}"
39
            );
40
        }
41
    }
42
43
    private function generateErrorText(): string
44
    {
45
        $lines = [];
46
        foreach ($this->fields as $key => $field) {
47
            $primary = $field->isPrimary() ? ' primary' : '';
48
            $line = sprintf("%s:\n  type=%s%s", $key, $field->getType(), $primary);
49
            // Print options
50
            foreach ($field->getOptions() as $optionName => $optionValue) {
51
                $line .= " {$optionName}=" . var_export($optionValue, true);
52
            }
53
            $lines[] = $line;
54
        }
55
        return implode("\n\n", $lines);
56
    }
57
58
    private function compareProperties(): bool
59
    {
60
        $tuples = array_map(static function (Field $field): array {
61
            return [
62
                $field->getType(),
63
                // $field->isPrimary(), // should not compared
64
            ];
65
        }, $this->fields);
66
67
        // Compare options content
68
        $prototype = array_shift($tuples);
69
        foreach ($tuples as $tuple) {
70
            if (count(array_diff_assoc($prototype, $tuple)) > 0) {
71
                return false;
72
            }
73
        }
74
        return true;
75
    }
76
77
    private function compareOptions(): bool
78
    {
79
        // Collect fields options
80
        $optionsSet = array_map(static function (Field $field): array {
81
            return iterator_to_array($field->getOptions());
82
        }, $this->fields);
83
84
        // Compare options cont
85
        $countResult = array_count_values(array_map('count', $optionsSet));
86
        if (count($countResult) !== 1) {
87
            return false;
88
        }
89
90
        // Compare options content
91
        $prototype = array_shift($optionsSet);
92
        foreach ($optionsSet as $options) {
93
            if (count(array_diff_assoc($prototype, $options)) > 0) {
94
                return false;
95
            }
96
        }
97
98
        return true;
99
    }
100
}
101