AttributeOverride::mergeRecursively()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
ccs 9
cts 9
cp 1
rs 9.2
cc 4
eloc 7
nc 4
nop 2
crap 4
1
<?php
2
3
namespace LaravelDoctrine\Fluent\Builders\Overrides;
4
5
use Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder;
6
use Doctrine\ORM\Mapping\ClassMetadataInfo;
7
use Doctrine\ORM\Mapping\NamingStrategy;
8
use InvalidArgumentException;
9
use LaravelDoctrine\Fluent\Buildable;
10
use LaravelDoctrine\Fluent\Builders\Field;
11
12
class AttributeOverride implements Buildable
13
{
14
    /**
15
     * @var string
16
     */
17
    protected $name;
18
19
    /**
20
     * @var callable
21
     */
22
    protected $callback;
23
24
    /**
25
     * @var ClassMetadataBuilder
26
     */
27
    protected $builder;
28
29
    /**
30
     * @var NamingStrategy
31
     */
32
    protected $namingStrategy;
33
34
    /**
35
     * @param ClassMetadataBuilder $builder
36
     * @param NamingStrategy       $namingStrategy
37
     * @param string               $name
38
     * @param callable             $callback
39
     */
40 9 View Code Duplication
    public function __construct(
41
        ClassMetadataBuilder $builder,
42
        NamingStrategy $namingStrategy,
43
        $name,
44
        callable $callback
45
    ) {
46 9
        $this->builder        = $builder;
47 9
        $this->callback       = $callback;
48 9
        $this->name           = $name;
49 9
        $this->namingStrategy = $namingStrategy;
50 9
    }
51
52
    /**
53
     * Execute the build process
54
     */
55 8
    public function build()
56
    {
57 8
        $callback = $this->callback;
58
59
        // We will create a new class metadata builder instance,
60
        // so we can use it to easily generated a new mapping
61
        // array, without re-declaring the existing field
62 8
        $builder = $this->newClassMetadataBuilder();
63
64 8
        $source = $this->convertToMappingArray($this->builder);
65
66
        // Create a new field builder for the new class metadata builder,
67
        // based on the existing (to be overridden) field
68 7
        $fieldBuilder = $this->getFieldBuilder(
69 7
            $builder,
70
            $source
71 7
        );
72
73 7
        $field = $callback($fieldBuilder);
74
75
        // When the user forget to return, use the Field instance
76
        // which contains the same information
77 7
        $field = $field ?: $fieldBuilder;
78
79 7
        if (!$field instanceof Field) {
80 1
            throw new InvalidArgumentException("The callback should return an instance of " . Field::class);
81
        }
82
83 6
        $field->build();
84
85 6
        $target = $this->convertToMappingArray($builder);
86
87 6
        $this->builder->getClassMetadata()->setAttributeOverride(
88 6
            $this->name,
89 6
            $this->mergeRecursively($source, $target)
90 6
        );
91 6
    }
92
93
    /**
94
     * @param ClassMetadataBuilder $builder
95
     * @param array                $mapping
96
     *
97
     * @return Field
98
     */
99 7
    protected function getFieldBuilder(ClassMetadataBuilder $builder, array $mapping)
100
    {
101 7
        return Field::make(
102 7
            $builder,
103 7
            $mapping['type'],
104 7
            $this->name
105 7
        );
106
    }
107
108
    /**
109
     * @param ClassMetadataBuilder $builder
110
     *
111
     * @throws \Doctrine\ORM\Mapping\MappingException
112
     * @return array
113
     */
114 8
    protected function convertToMappingArray(ClassMetadataBuilder $builder)
115
    {
116 8
        $metadata = $builder->getClassMetadata();
117
118 8
        return $metadata->getFieldMapping($this->name);
119
    }
120
121
    /**
122
     * @return ClassMetadataBuilder
123
     */
124 8
    protected function newClassMetadataBuilder()
125
    {
126 8
        return new ClassMetadataBuilder(
127 8
            new ClassMetadataInfo($this->builder->getClassMetadata()->name)
128 8
        );
129
    }
130
131
    /**
132
     * Merges the field mappings recursively, by keeping originals
133
     * settings, but replacing and adding new once
134
     *
135
     * @param array $source
136
     * @param array $target
137
     *
138
     * @return array
139
     */
140 6
    protected function mergeRecursively(array $source, array $target)
141
    {
142 6
        foreach ($source as $key => $value) {
143 6
            if (!isset($target[$key])) {
144 2
                $target[$key] = $value;
145 6
            } elseif (is_array($value)) {
146 1
                $target[$key] = $this->mergeRecursively($value, $target[$key]);
147 1
            }
148 6
        }
149
150 6
        return $target;
151
    }
152
}
153