| Total Complexity | 44 |
| Total Lines | 321 |
| Duplicated Lines | 0 % |
| Changes | 0 | ||
Complex classes like AbstractDomainObject often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use AbstractDomainObject, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 30 | abstract class AbstractDomainObject implements DomainObjectInterface, ObjectMonitoringInterface |
||
| 31 | { |
||
| 32 | /** |
||
| 33 | * @var int The uid of the record. The uid is only unique in the context of the database table. |
||
| 34 | */ |
||
| 35 | protected $uid; |
||
| 36 | |||
| 37 | /** |
||
| 38 | * @var int The uid of the localized record. Holds the uid of the record in default language (the translationOrigin). |
||
| 39 | */ |
||
| 40 | protected $_localizedUid; |
||
| 41 | |||
| 42 | /** |
||
| 43 | * @var int The uid of the language of the object. This is the uid of the language record in the table sys_language. |
||
| 44 | */ |
||
| 45 | protected $_languageUid; |
||
| 46 | |||
| 47 | /** |
||
| 48 | * @var int The uid of the versioned record. |
||
| 49 | */ |
||
| 50 | protected $_versionedUid; |
||
| 51 | |||
| 52 | /** |
||
| 53 | * @var int The id of the page the record is "stored". |
||
| 54 | */ |
||
| 55 | protected $pid; |
||
| 56 | |||
| 57 | /** |
||
| 58 | * TRUE if the object is a clone |
||
| 59 | * |
||
| 60 | * @var bool |
||
| 61 | */ |
||
| 62 | private $_isClone = false; |
||
| 63 | |||
| 64 | /** |
||
| 65 | * @var array An array holding the clean property values. Set right after reconstitution of the object |
||
| 66 | */ |
||
| 67 | private $_cleanProperties = []; |
||
| 68 | |||
| 69 | /** |
||
| 70 | * Getter for uid. |
||
| 71 | * |
||
| 72 | * @return int|null The uid or NULL if none set yet. |
||
| 73 | */ |
||
| 74 | public function getUid(): ?int |
||
| 75 | { |
||
| 76 | if ($this->uid !== null) { |
||
| 77 | return (int)$this->uid; |
||
| 78 | } |
||
| 79 | return null; |
||
| 80 | } |
||
| 81 | |||
| 82 | /** |
||
| 83 | * Setter for the pid. |
||
| 84 | * |
||
| 85 | * @param int $pid |
||
| 86 | */ |
||
| 87 | public function setPid(int $pid): void |
||
| 88 | { |
||
| 89 | $this->pid = $pid; |
||
| 90 | } |
||
| 91 | |||
| 92 | /** |
||
| 93 | * Getter for the pid. |
||
| 94 | * |
||
| 95 | * @return int|null The pid or NULL if none set yet. |
||
| 96 | */ |
||
| 97 | public function getPid(): ?int |
||
| 98 | { |
||
| 99 | if ($this->pid === null) { |
||
| 100 | return null; |
||
| 101 | } |
||
| 102 | return (int)$this->pid; |
||
| 103 | } |
||
| 104 | |||
| 105 | /** |
||
| 106 | * Reconstitutes a property. Only for internal use. |
||
| 107 | * |
||
| 108 | * @param string $propertyName |
||
| 109 | * @param mixed $propertyValue |
||
| 110 | * @return bool |
||
| 111 | * @internal |
||
| 112 | */ |
||
| 113 | public function _setProperty(string $propertyName, $propertyValue) |
||
| 114 | { |
||
| 115 | if ($this->_hasProperty($propertyName)) { |
||
| 116 | $this->{$propertyName} = $propertyValue; |
||
| 117 | return true; |
||
| 118 | } |
||
| 119 | return false; |
||
| 120 | } |
||
| 121 | |||
| 122 | /** |
||
| 123 | * Returns the property value of the given property name. Only for internal use. |
||
| 124 | * |
||
| 125 | * @param string $propertyName |
||
| 126 | * @return mixed The propertyValue |
||
| 127 | * @internal |
||
| 128 | */ |
||
| 129 | public function _getProperty(string $propertyName) |
||
| 130 | { |
||
| 131 | return $this->{$propertyName}; |
||
| 132 | } |
||
| 133 | |||
| 134 | /** |
||
| 135 | * Returns a hash map of property names and property values. Only for internal use. |
||
| 136 | * |
||
| 137 | * @return array The properties |
||
| 138 | * @internal |
||
| 139 | */ |
||
| 140 | public function _getProperties(): array |
||
| 141 | { |
||
| 142 | $properties = get_object_vars($this); |
||
| 143 | foreach ($properties as $propertyName => $propertyValue) { |
||
| 144 | if ($propertyName[0] === '_') { |
||
| 145 | unset($properties[$propertyName]); |
||
| 146 | } |
||
| 147 | } |
||
| 148 | return $properties; |
||
| 149 | } |
||
| 150 | |||
| 151 | /** |
||
| 152 | * Returns the property value of the given property name. Only for internal use. |
||
| 153 | * |
||
| 154 | * @param string $propertyName |
||
| 155 | * @return bool TRUE bool true if the property exists, FALSE if it doesn't exist or NULL in case of an error. |
||
| 156 | * @internal |
||
| 157 | */ |
||
| 158 | public function _hasProperty($propertyName) |
||
| 159 | { |
||
| 160 | return property_exists($this, $propertyName); |
||
| 161 | } |
||
| 162 | |||
| 163 | /** |
||
| 164 | * Returns TRUE if the object is new (the uid was not set, yet). Only for internal use |
||
| 165 | * |
||
| 166 | * @return bool |
||
| 167 | * @internal |
||
| 168 | */ |
||
| 169 | public function _isNew(): bool |
||
| 170 | { |
||
| 171 | return $this->uid === null; |
||
| 172 | } |
||
| 173 | |||
| 174 | /** |
||
| 175 | * Register an object's clean state, e.g. after it has been reconstituted |
||
| 176 | * from the database. |
||
| 177 | * |
||
| 178 | * @param string $propertyName The name of the property to be memorized. If omitted all persistable properties are memorized. |
||
| 179 | */ |
||
| 180 | public function _memorizeCleanState($propertyName = null) |
||
| 181 | { |
||
| 182 | if ($propertyName !== null) { |
||
| 183 | $this->_memorizePropertyCleanState($propertyName); |
||
| 184 | } else { |
||
| 185 | $this->_cleanProperties = []; |
||
| 186 | $properties = get_object_vars($this); |
||
| 187 | foreach ($properties as $propertyName => $propertyValue) { |
||
| 188 | if ($propertyName[0] === '_') { |
||
| 189 | continue; |
||
| 190 | } |
||
| 191 | // Do not memorize "internal" properties |
||
| 192 | $this->_memorizePropertyCleanState($propertyName); |
||
| 193 | } |
||
| 194 | } |
||
| 195 | } |
||
| 196 | |||
| 197 | /** |
||
| 198 | * Register a properties's clean state, e.g. after it has been reconstituted |
||
| 199 | * from the database. |
||
| 200 | * |
||
| 201 | * @param string $propertyName The name of the property to be memorized. If omitted all persistable properties are memorized. |
||
| 202 | */ |
||
| 203 | public function _memorizePropertyCleanState($propertyName) |
||
| 204 | { |
||
| 205 | $propertyValue = $this->{$propertyName}; |
||
| 206 | if (is_object($propertyValue)) { |
||
| 207 | $this->_cleanProperties[$propertyName] = clone $propertyValue; |
||
| 208 | // We need to make sure the clone and the original object |
||
| 209 | // are identical when compared with == (see _isDirty()). |
||
| 210 | // After the cloning, the Domain Object will have the property |
||
| 211 | // "isClone" set to TRUE, so we manually have to set it to FALSE |
||
| 212 | // again. Possible fix: Somehow get rid of the "isClone" property, |
||
| 213 | // which is currently needed in Fluid. |
||
| 214 | if ($propertyValue instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject) { |
||
| 215 | $this->_cleanProperties[$propertyName]->_setClone(false); |
||
| 216 | } |
||
| 217 | } else { |
||
| 218 | $this->_cleanProperties[$propertyName] = $propertyValue; |
||
| 219 | } |
||
| 220 | } |
||
| 221 | |||
| 222 | /** |
||
| 223 | * Returns a hash map of clean properties and $values. |
||
| 224 | * |
||
| 225 | * @return array |
||
| 226 | */ |
||
| 227 | public function _getCleanProperties() |
||
| 228 | { |
||
| 229 | return $this->_cleanProperties; |
||
| 230 | } |
||
| 231 | |||
| 232 | /** |
||
| 233 | * Returns the clean value of the given property. The returned value will be NULL if the clean state was not memorized before, or |
||
| 234 | * if the clean value is NULL. |
||
| 235 | * |
||
| 236 | * @param string $propertyName The name of the property to be memorized. |
||
| 237 | * @return mixed The clean property value or NULL |
||
| 238 | * @internal |
||
| 239 | */ |
||
| 240 | public function _getCleanProperty(string $propertyName) |
||
| 241 | { |
||
| 242 | return $this->_cleanProperties[$propertyName] ?? null; |
||
| 243 | } |
||
| 244 | |||
| 245 | /** |
||
| 246 | * Returns TRUE if the properties were modified after reconstitution |
||
| 247 | * |
||
| 248 | * @param string $propertyName An optional name of a property to be checked if its value is dirty |
||
| 249 | * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\TooDirtyException |
||
| 250 | * @return bool |
||
| 251 | */ |
||
| 252 | public function _isDirty($propertyName = null) |
||
| 253 | { |
||
| 254 | if ($this->uid !== null && $this->_getCleanProperty('uid') !== null && $this->uid != $this->_getCleanProperty('uid')) { |
||
| 255 | throw new TooDirtyException('The uid "' . $this->uid . '" has been modified, that is simply too much.', 1222871239); |
||
| 256 | } |
||
| 257 | |||
| 258 | if ($propertyName === null) { |
||
| 259 | foreach ($this->_getCleanProperties() as $propertyName => $cleanPropertyValue) { |
||
| 260 | if ($this->isPropertyDirty($cleanPropertyValue, $this->{$propertyName}) === true) { |
||
| 261 | return true; |
||
| 262 | } |
||
| 263 | } |
||
| 264 | } else { |
||
| 265 | if ($this->isPropertyDirty($this->_getCleanProperty($propertyName), $this->{$propertyName}) === true) { |
||
| 266 | return true; |
||
| 267 | } |
||
| 268 | } |
||
| 269 | return false; |
||
| 270 | } |
||
| 271 | |||
| 272 | /** |
||
| 273 | * Checks the $value against the $cleanState. |
||
| 274 | * |
||
| 275 | * @param mixed $previousValue |
||
| 276 | * @param mixed $currentValue |
||
| 277 | * @return bool |
||
| 278 | */ |
||
| 279 | protected function isPropertyDirty($previousValue, $currentValue) |
||
| 280 | { |
||
| 281 | // In case it is an object and it implements the ObjectMonitoringInterface, we call _isDirty() instead of a simple comparison of objects. |
||
| 282 | // We do this, because if the object itself contains a lazy loaded property, the comparison of the objects might fail even if the object didn't change |
||
| 283 | if (is_object($currentValue)) { |
||
| 284 | $currentTypeString = null; |
||
| 285 | if ($currentValue instanceof LazyLoadingProxy) { |
||
| 286 | $currentTypeString = $currentValue->_getTypeAndUidString(); |
||
| 287 | } elseif ($currentValue instanceof DomainObjectInterface) { |
||
| 288 | $currentTypeString = get_class($currentValue) . ':' . $currentValue->getUid(); |
||
| 289 | } |
||
| 290 | |||
| 291 | if ($currentTypeString !== null) { |
||
| 292 | $previousTypeString = null; |
||
| 293 | if ($previousValue instanceof LazyLoadingProxy) { |
||
| 294 | $previousTypeString = $previousValue->_getTypeAndUidString(); |
||
| 295 | } elseif ($previousValue instanceof DomainObjectInterface) { |
||
| 296 | $previousTypeString = get_class($previousValue) . ':' . $previousValue->getUid(); |
||
| 297 | } |
||
| 298 | |||
| 299 | $result = $currentTypeString !== $previousTypeString; |
||
| 300 | } elseif ($currentValue instanceof ObjectMonitoringInterface) { |
||
| 301 | $result = !is_object($previousValue) || $currentValue->_isDirty() || get_class($previousValue) !== get_class($currentValue); |
||
| 302 | } else { |
||
| 303 | // For all other objects we do only a simple comparison (!=) as we want cloned objects to return the same values. |
||
| 304 | $result = $previousValue != $currentValue; |
||
| 305 | } |
||
| 306 | } else { |
||
| 307 | $result = $previousValue !== $currentValue; |
||
| 308 | } |
||
| 309 | return $result; |
||
| 310 | } |
||
| 311 | |||
| 312 | /** |
||
| 313 | * Returns TRUE if the object has been cloned, FALSE otherwise. |
||
| 314 | * |
||
| 315 | * @return bool TRUE if the object has been cloned |
||
| 316 | */ |
||
| 317 | public function _isClone() |
||
| 320 | } |
||
| 321 | |||
| 322 | /** |
||
| 323 | * Setter whether this Domain Object is a clone of another one. |
||
| 324 | * NEVER SET THIS PROPERTY DIRECTLY. We currently need it to make the |
||
| 325 | * _isDirty check inside AbstractEntity work, but it is just a work- |
||
| 326 | * around right now. |
||
| 327 | * |
||
| 328 | * @param bool $clone |
||
| 329 | */ |
||
| 330 | public function _setClone($clone) |
||
| 331 | { |
||
| 332 | $this->_isClone = (bool)$clone; |
||
| 333 | } |
||
| 334 | |||
| 335 | /** |
||
| 336 | * Clone method. Sets the _isClone property. |
||
| 337 | */ |
||
| 338 | public function __clone() |
||
| 339 | { |
||
| 340 | $this->_isClone = true; |
||
| 341 | } |
||
| 342 | |||
| 343 | /** |
||
| 344 | * Returns the class name and the uid of the object as string |
||
| 345 | * |
||
| 346 | * @return string |
||
| 347 | */ |
||
| 348 | public function __toString() |
||
| 351 | } |
||
| 352 | } |
||
| 353 |