Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
1 | <?php |
||
14 | class AssociationOverride implements Buildable |
||
15 | { |
||
16 | /** |
||
17 | * @var string |
||
18 | */ |
||
19 | protected $name; |
||
20 | |||
21 | /** |
||
22 | * @var callable |
||
23 | */ |
||
24 | protected $callback; |
||
25 | |||
26 | /** |
||
27 | * @var ClassMetadataBuilder |
||
28 | */ |
||
29 | protected $builder; |
||
30 | |||
31 | /** |
||
32 | * @var NamingStrategy |
||
33 | */ |
||
34 | protected $namingStrategy; |
||
35 | |||
36 | /** |
||
37 | * @var array |
||
38 | */ |
||
39 | protected $relations = [ |
||
40 | ClassMetadataInfo::MANY_TO_ONE => ManyToOne::class, |
||
41 | ClassMetadataInfo::MANY_TO_MANY => ManyToMany::class |
||
42 | ]; |
||
43 | |||
44 | /** |
||
45 | * @param ClassMetadataBuilder $builder |
||
46 | * @param NamingStrategy $namingStrategy |
||
47 | * @param string $name |
||
48 | * @param callable $callback |
||
49 | */ |
||
50 | 13 | View Code Duplication | public function __construct( |
51 | ClassMetadataBuilder $builder, |
||
52 | NamingStrategy $namingStrategy, |
||
53 | $name, |
||
54 | callable $callback |
||
55 | ) { |
||
56 | 13 | $this->builder = $builder; |
|
57 | 13 | $this->callback = $callback; |
|
58 | 13 | $this->name = $name; |
|
59 | 13 | $this->namingStrategy = $namingStrategy; |
|
60 | 13 | } |
|
61 | |||
62 | /** |
||
63 | * Execute the build process |
||
64 | */ |
||
65 | 12 | public function build() |
|
66 | { |
||
67 | 12 | $callback = $this->callback; |
|
68 | |||
69 | // We will create a new class metadata builder instance, |
||
70 | // so we can use it to easily generated a new mapping |
||
71 | // array, without re-declaring the existing association |
||
72 | 12 | $builder = $this->newClassMetadataBuilder(); |
|
73 | 12 | $source = $this->convertToMappingArray($this->builder); |
|
74 | |||
75 | 11 | if (!isset($this->relations[$source['type']])) { |
|
76 | 1 | throw new InvalidArgumentException('Only ManyToMany and ManyToOne relations can be overridden'); |
|
77 | } |
||
78 | |||
79 | // Create a new association builder, based on the given type |
||
80 | 10 | $associationBuilder = $this->getAssociationBuilder($builder, $source); |
|
81 | |||
82 | // Give the original join table name, so we won't |
||
83 | // accidentally remove custom join table names |
||
84 | 10 | if ($this->hasJoinTable($source)) { |
|
85 | 7 | $associationBuilder->setJoinTable($source['joinTable']['name']); |
|
86 | 7 | } |
|
87 | |||
88 | 10 | $association = $callback($associationBuilder); |
|
89 | |||
90 | // When the user forget to return, use the $associationBuilder instance |
||
91 | // which contains the same information |
||
92 | 10 | $association = $association ?: $associationBuilder; |
|
93 | |||
94 | 10 | if (!$association instanceof Relation) { |
|
95 | 1 | throw new InvalidArgumentException("The callback should return an instance of " . Relation::class); |
|
96 | } |
||
97 | |||
98 | 9 | $association->build(); |
|
99 | |||
100 | 9 | $target = $this->convertToMappingArray($builder); |
|
101 | |||
102 | 9 | $overrideMapping = []; |
|
103 | |||
104 | // ManyToMany mappings |
||
105 | 9 | if ($this->hasJoinTable($target)) { |
|
106 | 6 | $overrideMapping['joinTable'] = $this->mapJoinTable( |
|
107 | 6 | $target['joinTable'], |
|
108 | 6 | $source['joinTable'] |
|
109 | 6 | ); |
|
110 | 6 | } |
|
111 | |||
112 | // ManyToOne mappings |
||
113 | 9 | View Code Duplication | if ($this->hasJoinColumns($target)) { |
114 | 3 | $overrideMapping['joinColumns'] = $this->mapJoinColumns( |
|
115 | 3 | $target['joinColumns'], |
|
116 | 3 | $source['joinColumns'] |
|
117 | 3 | ); |
|
118 | 3 | } |
|
119 | |||
120 | 9 | $this->builder->getClassMetadata()->setAssociationOverride( |
|
121 | 9 | $this->name, |
|
122 | $overrideMapping |
||
123 | 9 | ); |
|
124 | 9 | } |
|
125 | |||
126 | /** |
||
127 | * @param ClassMetadataBuilder $builder |
||
128 | * |
||
129 | * @throws \Doctrine\ORM\Mapping\MappingException |
||
130 | * @return array |
||
131 | */ |
||
132 | 12 | protected function convertToMappingArray(ClassMetadataBuilder $builder) |
|
133 | { |
||
134 | 12 | $metadata = $builder->getClassMetadata(); |
|
135 | |||
136 | 12 | return $metadata->getAssociationMapping($this->name); |
|
137 | } |
||
138 | |||
139 | /** |
||
140 | * @return ClassMetadataBuilder |
||
141 | */ |
||
142 | 12 | protected function newClassMetadataBuilder() |
|
143 | { |
||
144 | 12 | return new ClassMetadataBuilder( |
|
145 | 12 | new ClassMetadataInfo($this->builder->getClassMetadata()->name) |
|
146 | 12 | ); |
|
147 | } |
||
148 | |||
149 | /** |
||
150 | * @param $builder |
||
151 | * @param $source |
||
152 | * |
||
153 | * @return mixed |
||
154 | */ |
||
155 | 10 | protected function getAssociationBuilder(ClassMetadataBuilder $builder, array $source) |
|
156 | { |
||
157 | 10 | return new $this->relations[$source['type']]( |
|
158 | 10 | $builder, |
|
159 | 10 | $this->namingStrategy, |
|
160 | 10 | $this->name, |
|
161 | 10 | $source['targetEntity'] |
|
162 | 10 | ); |
|
163 | } |
||
164 | |||
165 | /** |
||
166 | * @param array $target |
||
167 | * @param array $source |
||
168 | * |
||
169 | * @return array |
||
170 | */ |
||
171 | 6 | protected function mapJoinTable(array $target = [], array $source = []) |
|
172 | { |
||
173 | 6 | $joinTable['name'] = $target['name']; |
|
174 | |||
175 | 6 | View Code Duplication | if ($this->hasJoinColumns($target)) { |
176 | 6 | $joinTable['joinColumns'] = $this->mapJoinColumns( |
|
177 | 6 | $target['joinColumns'], |
|
178 | 6 | $source['joinColumns'] |
|
179 | 6 | ); |
|
180 | 6 | } |
|
181 | |||
182 | 6 | if ($this->hasInverseJoinColumns($target)) { |
|
183 | 6 | $joinTable['inverseJoinColumns'] = $this->mapJoinColumns( |
|
184 | 6 | $target['inverseJoinColumns'], |
|
185 | 6 | $source['inverseJoinColumns'] |
|
186 | 6 | ); |
|
187 | 6 | } |
|
188 | |||
189 | 6 | return $joinTable; |
|
190 | } |
||
191 | |||
192 | /** |
||
193 | * @param array $target |
||
194 | * @param array $source |
||
195 | * |
||
196 | * @return mixed |
||
197 | * @internal param $target |
||
198 | * @internal param $source |
||
199 | * @internal param $overrideMapping |
||
200 | */ |
||
201 | 9 | protected function mapJoinColumns(array $target = [], array $source = []) |
|
216 | |||
217 | /** |
||
218 | * @param array $target |
||
219 | * |
||
220 | * @return bool |
||
221 | */ |
||
222 | 9 | protected function hasJoinColumns(array $target = []) |
|
226 | |||
227 | /** |
||
228 | * @param array $target |
||
229 | * |
||
230 | * @return bool |
||
231 | */ |
||
232 | 6 | protected function hasInverseJoinColumns(array $target = []) |
|
236 | |||
237 | /** |
||
238 | * @param array $target |
||
239 | * |
||
240 | * @return bool |
||
241 | */ |
||
242 | 10 | protected function hasJoinTable(array $target = []) |
|
246 | } |
||
247 |