Passed
Pull Request — master (#132)
by Dorian
05:13
created

DirectForeignKeyMethodDescriptor::getMainTable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace TheCodingMachine\TDBM\Utils;
6
7
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
8
use Doctrine\DBAL\Schema\Table;
9
use TheCodingMachine\TDBM\AlterableResultIterator;
10
use TheCodingMachine\TDBM\Schema\ForeignKey;
11
use TheCodingMachine\TDBM\TDBMException;
12
use TheCodingMachine\TDBM\Utils\Annotation\AnnotationParser;
13
use TheCodingMachine\TDBM\Utils\Annotation;
14
use Zend\Code\Generator\AbstractMemberGenerator;
15
use Zend\Code\Generator\DocBlock\Tag\ReturnTag;
16
use Zend\Code\Generator\MethodGenerator;
17
18
/**
19
 * Represents a method to get a list of beans from a direct foreign key pointing to our bean.
20
 */
21
class DirectForeignKeyMethodDescriptor implements MethodDescriptorInterface
22
{
23
    use ForeignKeyAnalyzerTrait;
24
25
    /**
26
     * @var ForeignKeyConstraint
27
     */
28
    private $foreignKey;
29
30
    private $useAlternateName = false;
31
    /**
32
     * @var Table
33
     */
34
    private $mainTable;
35
    /**
36
     * @var NamingStrategyInterface
37
     */
38
    private $namingStrategy;
39
    /**
40
     * @var AnnotationParser
41
     */
42
    private $annotationParser;
43
44
    /**
45
     * @param ForeignKeyConstraint $fk The foreign key pointing to our bean
46
     * @param Table $mainTable The main table that is pointed to
47
     * @param NamingStrategyInterface $namingStrategy
48
     */
49
    public function __construct(ForeignKeyConstraint $fk, Table $mainTable, NamingStrategyInterface $namingStrategy, AnnotationParser $annotationParser)
50
    {
51
        $this->foreignKey = $fk;
52
        $this->mainTable = $mainTable;
53
        $this->namingStrategy = $namingStrategy;
54
        $this->annotationParser = $annotationParser;
55
    }
56
57
    /**
58
     * Returns the name of the method to be generated.
59
     *
60
     * @return string
61
     */
62
    public function getName() : string
63
    {
64
        if (!$this->useAlternateName) {
65
            return 'get'.TDBMDaoGenerator::toCamelCase($this->foreignKey->getLocalTableName());
66
        } else {
67
            $methodName = 'get'.TDBMDaoGenerator::toCamelCase($this->foreignKey->getLocalTableName()).'By';
68
69
            $camelizedColumns = array_map([TDBMDaoGenerator::class, 'toCamelCase'], $this->foreignKey->getUnquotedLocalColumns());
70
71
            $methodName .= implode('And', $camelizedColumns);
72
73
            return $methodName;
74
        }
75
    }
76
77
    /**
78
     * Returns the name of the class that will be returned by the getter (short name).
79
     *
80
     * @return string
81
     */
82
    public function getBeanClassName(): string
83
    {
84
        return $this->namingStrategy->getBeanClassName($this->foreignKey->getLocalTableName());
85
    }
86
87
    /**
88
     * Requests the use of an alternative name for this method.
89
     */
90
    public function useAlternativeName(): void
91
    {
92
        $this->useAlternateName = true;
93
    }
94
95
    /**
96
     * Returns the code of the method.
97
     *
98
     * @return MethodGenerator[]
99
     */
100
    public function getCode() : array
101
    {
102
        $beanClass = $this->getBeanClassName();
103
104
        $getter = new MethodGenerator($this->getName());
105
        $getter->setDocBlock(sprintf('Returns the list of %s pointing to this bean via the %s column.', $beanClass, implode(', ', $this->foreignKey->getUnquotedLocalColumns())));
106
        $getter->getDocBlock()->setTag(new ReturnTag([
107
            $beanClass.'[]',
108
            '\\'.AlterableResultIterator::class
109
        ]));
110
        $getter->setReturnType(AlterableResultIterator::class);
111
112
        $tdbmFk = ForeignKey::createFromFk($this->foreignKey);
113
114
        $code = sprintf(
115
            'return $this->retrieveManyToOneRelationshipsStorage(%s, %s, %s);',
116
            var_export($this->foreignKey->getLocalTableName(), true),
117
            var_export($tdbmFk->getCacheKey(), true),
118
            $this->getFilters($this->foreignKey)
119
        );
120
121
        $getter->setBody($code);
122
123
        if ($this->isProtected()) {
124
            $getter->setVisibility(AbstractMemberGenerator::VISIBILITY_PROTECTED);
125
        }
126
127
        return [ $getter ];
128
    }
129
130
    private function getFilters(ForeignKeyConstraint $fk) : string
131
    {
132
        $counter = 0;
133
        $parameters = [];
134
135
        $fkForeignColumns = $fk->getUnquotedForeignColumns();
136
137
        foreach ($fk->getUnquotedLocalColumns() as $columnName) {
138
            $fkColumn = $fkForeignColumns[$counter];
139
            $parameters[] = sprintf('%s => $this->get(%s, %s)', var_export($fk->getLocalTableName().'.'.$columnName, true), var_export($fkColumn, true), var_export($this->foreignKey->getForeignTableName(), true));
140
            ++$counter;
141
        }
142
        $parametersCode = '['.implode(', ', $parameters).']';
143
144
        return $parametersCode;
145
    }
146
147
    /**
148
     * Returns an array of classes that needs a "use" for this method.
149
     *
150
     * @return string[]
151
     */
152
    public function getUsedClasses() : array
153
    {
154
        return [$this->getBeanClassName()];
155
    }
156
157
    /**
158
     * Returns the code to past in jsonSerialize.
159
     *
160
     * @return string
161
     */
162
    public function getJsonSerializeCode() : string
163
    {
164
        $jsonCollection = $this->findAnnotation(Annotation\JsonCollection::class);
165
        if ($jsonCollection === null) {
166
            return '';
167
        }
168
169
        if ($jsonFormat = $this->findAnnotation(Annotation\JsonFormat::class)) {
170
            $method = $jsonFormat->method ?? 'get' . ucfirst($jsonFormat->property);
171
            $format = "$method()";
172
        } else {
173
            $stopRecursion = $this->findAnnotation(Annotation\JsonRecursive::class) ? '' : 'true';
174
            $format = "jsonSerialize($stopRecursion)";
175
        }
176
        $isIncluded = $this->findAnnotation(Annotation\JsonInclude::class) !== null;
177
        $index = $jsonCollection->key ?: lcfirst(TDBMDaoGenerator::toCamelCase($this->foreignKey->getLocalTableName()));
178
        $class = $this->getBeanClassName();
179
        $variableName = '$' . TDBMDaoGenerator::toVariableName($class);
180
        $getter = $this->getName();
181
        $code = <<<PHP
182
\$array['$index'] = array_map(function ($class $variableName) {
183
    return ${variableName}->$format;
184
}, \$this->$getter()->toArray());
185
PHP;
186
        if (!$isIncluded) {
187
            $code = preg_replace('(\n)', '\0    ', $code);
188
            $code = <<<PHP
189
if (!\$stopRecursion) {
190
    $code
191
};
192
PHP;
193
        }
194
        return $code;
195
    }
196
197
    /**
198
     * @return ForeignKeyConstraint
199
     */
200
    public function getForeignKey(): ForeignKeyConstraint
201
    {
202
        return $this->foreignKey;
203
    }
204
205
    /**
206
     * Returns the table that is pointed to.
207
     * @return Table
208
     */
209
    public function getMainTable(): Table
210
    {
211
        return $this->mainTable;
212
    }
213
214
    private function isProtected(): bool
215
    {
216
        return $this->findAnnotation(Annotation\ProtectedOneToMany::class) !== null;
217
    }
218
219
    private function findAnnotation(string $type)
220
    {
221
        foreach ($this->getAnnotations() as $annotations) {
222
            $annotation = $annotations->findAnnotation($type);
223
            if ($annotation !== null) {
224
                return $annotation;
225
            }
226
        }
227
        return null;
228
    }
229
}
230