Test Failed
Push — master ( 087751...3d501f )
by George
03:59
created

ForeignKey::validate()   C

Complexity

Conditions 12
Paths 20

Size

Total Lines 106
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 106
rs 5.034
cc 12
eloc 42
nc 20
nop 0

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
namespace JsonTable\Analyse;
3
4
/**
5
 * Perform primary key analysis.
6
 *
7
 * @package JsonTable
8
 */
9
class ForeignKey extends Analyse implements AnalyseInterface
10
{
11
    /**
12
     * @var string The description for fields with invalid foreign keys.
13
     */
14
    const ERROR_INVALID_FOREIGN_KEY = 'There are <strong>%d</strong> fields that have invalid foreign keys:';
15
    
16
    
17
    /**
18
     * Validate that any specified foreign key constraints have been met.
19
     *
20
     * @access  public
21
     *
22
     * @return  boolean Does the data meet the foreign key constraints.
23
     *
24
     * @throws  \Exception if a foreign key other than postgresql is specified.
25
     */
26
    public function validate()
27
    {
28
        // Check that a primary key has been specified.
29
        if (false === property_exists(self::$schemaJson, 'foreignKeys')) {
30
            // There is no foreign key specified so validate as successfully passed.
31
            return true;
32
        }
33
34
        // Rewind the CSV file pointer to the first line of data.
35
        self::rewindFilePointerToFirstData();
36
37
        // Loop through the foreign keys.
38
        foreach (self::$schemaJson->foreignKeys as $foreignKey) {
39
            // Get the datapackage for this foreign key.
40
            $dataPackage = $this->getForeignKeyPackage($foreignKey);
41
42
            // Only "postgresql" datapackages are currently supported.
43
            if ('postgresql' !== $dataPackage) {
44
                throw new \Exception("Only postgresql foreign keys are currently supported.
45
                Please ensure that the datapackage attribute on all foreign keys is defined
46
                as &quot;database&quot; or is omitted.");
47
            }
48
49
            // Instantiate the foreign key validator for this datapackage type.
50
            $validator = $this->instantiateValidator(Analyse::VALIDATION_TYPE_FOREIGN_KEY, $dataPackage);
51
52
            // Get the fields in the CSV and the resource for this foreign key.
53
            $csvFields = (array) $foreignKey->fields;
54
            $referenceFields = (array) $foreignKey->reference->fields;
55
            $csvPositions = [];
56
57
            // Loop through the CSV fields listed in the foreign
58
            // key and build up a list of CSV positions these relate to.
59
            foreach ($csvFields as $csvFieldName) {
60
                // Ensure the field name is lowercase as all field names have been lower-cased.
61
                $csvFieldName = strtolower($csvFieldName);
62
63
                // Check that the field exists in the schema.
64
                if (false === $this->getSchemaKeyFromName($csvFieldName)) {
65
                    throw new \Exception("The foreign key field &quot;$csvFieldName&quot;
66
                    was not defined in the schema.");
67
                }
68
69
                $csvPosition = $this->getCsvPositionFromName($csvFieldName);
70
71
                // Get the position of this field in the CSV file.
72
                if (false === $csvPosition) {
73
                    // The field isn't in the CSV.
74
                    if (1 !== count($csvFields)) {
75
                        // This field is part of a multi field foreign key.
76
                        // Throw an error as this key cannot be validated.
77
                        throw new \Exception("The foreign key field &quot;$csvFieldName&quot;
78
                        was not in the CSV file but is required as part of a multi field foreign key.");
79
                    }
80
81
                    // This is the only field in the foreign key so skip the validation of this foreign key.
82
                    continue 2;
83
                }
84
85
                // Add the position of this foreign key related CSV field to the container
86
                // so the data for it can be retrieved.
87
                $csvPositions[] = $csvPosition;
88
            }
89
90
            // Set the row flag.
91
            $row = 1;
92
93
            // Read each row in the file.
94
            while ($csvRow = self::loopThroughFileRows()) {
95
                // Define the container for the foreign key parts for this row.
96
                $rowKeyParts = [];
97
98
                // Build up the CSV foreign key hash using the CSV field positions calculated above.
99
                foreach ($csvPositions as $csvPosition) {
100
                    $rowKeyParts[] = $csvRow[$csvPosition];
101
                }
102
103
                $csvValueHash = implode(', ', $rowKeyParts);
104
105
                // Validate the foreign key.
106
                if (!$validator->validate(
107
                    $csvValueHash,
108
                    $foreignKey->reference->resource,
109
                    $referenceFields
110
                )) {
111
                    // This hash didn't match a foreign key.
112
                    $csvFields = implode(', ', $csvFields);
113
                    $errorMessage = "The value(s) of &quot;$csvValueHash&quot; in column(s) $csvFields
114
                    on row $row doesn't match a foreign key.";
115
116
                    $this->setError(Analyse::ERROR_INVALID_FOREIGN_KEY, $errorMessage);
117
                    $this->statistics['rows_with_errors'][] = $row;
118
119
                    if ($this->stopIfInvalid) {
120
                        return false;
121
                    }
122
                }
123
124
                $row++;
125
            }
126
127
            self::rewindFilePointerToFirstData();
128
        }
129
130
        return true;
131
    }
132
133
    
134
    /**
135
     * Get the package of the specified foreign key.
136
     *
137
     * @access  private
138
     *
139
     * @param   object  $foreignKey The foreign key object to examine.
140
     *
141
     * @return  string  The package for the foreign key.
142
     */
143
    private function getForeignKeyPackage($foreignKey)
144
    {
145
        $propertyExists = property_exists($foreignKey->reference, 'datapackage');
146
        return $propertyExists ? $foreignKey->reference->datapackage : 'postgresql';
147
    }
148
}