Completed
Push — wip/steps ( 87442c...c45d26 )
by Romain
03:53
created

FormMetadataObject::has()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
/*
3
 * 2017 Romain CANON <[email protected]>
4
 *
5
 * This file is part of the TYPO3 FormZ project.
6
 * It is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU General Public License, either
8
 * version 3 of the License, or any later version.
9
 *
10
 * For the full copyright and license information, see:
11
 * http://www.gnu.org/licenses/gpl-3.0.html
12
 */
13
14
namespace Romm\Formz\Domain\Model\DataObject;
15
16
use Romm\Formz\Core\Core;
17
use Romm\Formz\Domain\Model\FormMetadata;
18
use Romm\Formz\Domain\Repository\FormMetadataRepository;
19
use Romm\Formz\Exceptions\EntryNotFoundException;
20
use TYPO3\CMS\Core\Type\TypeInterface;
21
use TYPO3\CMS\Core\Utility\ArrayUtility;
22
23
/**
24
 * Store and retrieve arbitrary data using the setter/getter functions.
25
 */
26
class FormMetadataObject implements TypeInterface
27
{
28
    const DATA_SET = 'set';
29
    const DATA_REMOVED = 'removed';
30
31
    /**
32
     * @var array
33
     */
34
    protected $metadata = [];
35
36
    /**
37
     * @var FormMetadata
38
     */
39
    protected $object;
40
41
    /**
42
     * Contains a list of data keys that have been manipulated by the methods of
43
     * this class.
44
     *
45
     * This is used to keep a trace of what was changed, to handle database
46
     * manipulation that was done during the runtime of this request.
47
     *
48
     * @var array
49
     */
50
    protected $touchedData = [];
51
52
    /**
53
     * @param string $data
54
     */
55
    public function __construct($data = null)
56
    {
57
        if ($data) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $data of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
58
            $data = unserialize($data);
59
60
            if (is_array($data)
61
                && isset($data['metadata'])
62
            ) {
63
                $this->metadata = $data['metadata'];
64
            }
65
        }
66
    }
67
68
    /**
69
     * @param string $key
70
     * @return mixed
71
     * @throws EntryNotFoundException
72
     */
73
    public function get($key)
74
    {
75
        if (false === $this->has($key)) {
76
            throw EntryNotFoundException::metadataNotFound($key);
77
        }
78
79
        return ArrayUtility::getValueByPath($this->metadata, $key, '.');
80
    }
81
82
    /**
83
     * @param string $key
84
     * @return bool
85
     */
86
    public function has($key)
87
    {
88
        return ArrayUtility::isValidPath($this->metadata, $key, '.');
89
    }
90
91
    /**
92
     * @param string $key
93
     * @param mixed  $value
94
     */
95
    public function set($key, $value)
96
    {
97
        $this->metadata = ArrayUtility::setValueByPath($this->metadata, $key, $value, '.');
98
99
        $this->touchedData[$key] = self::DATA_SET;
100
    }
101
102
    /**
103
     * @param string $key
104
     */
105
    public function remove($key)
106
    {
107
        if ($this->has($key)) {
108
            $this->metadata = ArrayUtility::removeByPath($this->metadata, $key, '.');
109
110
            $this->touchedData[$key] = self::DATA_REMOVED;
111
        }
112
    }
113
114
    /**
115
     * Persists the last changes of this instance in database.
116
     */
117
    public function persist()
118
    {
119
        $persistenceManager = Core::get()->getPersistenceManager();
120
121
        if (null === $this->object->getUid()) {
122
            /** @var FormMetadataRepository $formMetadataRepository */
123
            $formMetadataRepository = Core::instantiate(FormMetadataRepository::class);
124
125
            $object = $formMetadataRepository->findOneByHash($this->object->getHash());
126
127
            if ($object) {
128
                $this->object = $object;
129
130
                /*
131
                 * If any data was manipulated during the runtime, it is updated
132
                 * in the current metadata object.
133
                 */
134
                if (!empty($this->touchedData)) {
135
                    foreach ($this->touchedData as $key => $type) {
136
                        if ($type === self::DATA_SET) {
137
                            $this->object->getMetadata()->set($key, $this->get($key));
138
                        } elseif ($type === self::DATA_REMOVED) {
139
                            $this->object->getMetadata()->remove($key);
140
                        }
141
                    }
142
143
                    $persistenceManager->update($this->object);
144
                }
145
            } else {
146
                $persistenceManager->add($this->object);
147
            }
148
        } else {
149
            $persistenceManager->update($this->object);
150
        }
151
152
        $persistenceManager->persistAll();
153
    }
154
155
    /**
156
     * @return array
157
     */
158
    public function getMetadata()
159
    {
160
        return $this->metadata;
161
    }
162
163
    /**
164
     * @param FormMetadata $metadata
165
     */
166
    public function setObject(FormMetadata $metadata)
167
    {
168
        if (null !== $this->object) {
169
            throw new \Exception('todo'); // @todo
170
        }
171
172
        $this->object = $metadata;
173
    }
174
175
    /**
176
     * @return string
177
     */
178
    public function __toString()
179
    {
180
        return serialize(['metadata' => $this->metadata]);
181
    }
182
183
    /**
184
     * @see deepClone()
185
     */
186
    public function __clone()
187
    {
188
        $this->deepClone($this->metadata);
189
    }
190
191
    /**
192
     * When the metadata is fetched from persistence, the `metadata` array can
193
     * contain object instances, meaning that by default the original references
194
     * to these objects are used in the clean properties, resulting in the
195
     * object modification not being detected.
196
     *
197
     * In this function, we clone every object that is found, to solve the issue
198
     * above.
199
     *
200
     * @param mixed $entry
201
     * @param array $path
202
     */
203
    protected function deepClone($entry, array $path = [])
204
    {
205
        if (is_array($entry)) {
206
            foreach ($entry as $key => $item) {
207
                $path[] = $key;
208
                $this->deepClone($item, $path);
209
            }
210
        } elseif (is_object($entry)) {
211
            $this->set(implode('.', $path), clone $entry);
212
        }
213
    }
214
}
215