vendor/pimcore/pimcore/models/DataObject/ClassDefinition.php line 273

Open in your IDE?
  1. <?php
  2. /**
  3. * Pimcore
  4. *
  5. * This source file is available under two different licenses:
  6. * - GNU General Public License version 3 (GPLv3)
  7. * - Pimcore Commercial License (PCL)
  8. * Full copyright and license information is available in
  9. * LICENSE.md which is distributed with this source code.
  10. *
  11. * @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12. * @license http://www.pimcore.org/license GPLv3 and PCL
  13. */
  14. namespace Pimcore\Model\DataObject;
  15. use Pimcore\Cache;
  16. use Pimcore\Db;
  17. use Pimcore\Event\DataObjectClassDefinitionEvents;
  18. use Pimcore\Event\Model\DataObject\ClassDefinitionEvent;
  19. use Pimcore\File;
  20. use Pimcore\Logger;
  21. use Pimcore\Model;
  22. use Pimcore\Model\DataObject;
  23. use Pimcore\Model\DataObject\ClassDefinition\Data\FieldDefinitionEnrichmentInterface;
  24. /**
  25. * @method \Pimcore\Model\DataObject\ClassDefinition\Dao getDao()
  26. */
  27. final class ClassDefinition extends Model\AbstractModel
  28. {
  29. use DataObject\ClassDefinition\Helper\VarExport;
  30. use DataObject\Traits\LocateFileTrait;
  31. /**
  32. * @internal
  33. *
  34. * @var string|null
  35. */
  36. public $id;
  37. /**
  38. * @internal
  39. *
  40. * @var string|null
  41. */
  42. public $name;
  43. /**
  44. * @internal
  45. *
  46. * @var string
  47. */
  48. public $description = '';
  49. /**
  50. * @internal
  51. *
  52. * @var int
  53. */
  54. public $creationDate = 0;
  55. /**
  56. * @internal
  57. *
  58. * @var int
  59. */
  60. public $modificationDate = 0;
  61. /**
  62. * @internal
  63. *
  64. * @var int
  65. */
  66. public $userOwner = 0;
  67. /**
  68. * @internal
  69. *
  70. * @var int
  71. */
  72. public $userModification = 0;
  73. /**
  74. * @internal
  75. *
  76. * @var string
  77. */
  78. public $parentClass = '';
  79. /**
  80. * Comma separated list of interfaces
  81. *
  82. * @internal
  83. *
  84. * @var string|null
  85. */
  86. public $implementsInterfaces;
  87. /**
  88. * Name of the listing parent class if set
  89. *
  90. * @internal
  91. *
  92. * @var string
  93. */
  94. public $listingParentClass = '';
  95. /**
  96. * @internal
  97. *
  98. * @var string
  99. */
  100. public $useTraits = '';
  101. /**
  102. * @internal
  103. *
  104. * @var string
  105. */
  106. public $listingUseTraits = '';
  107. /**
  108. * @internal
  109. *
  110. * @var bool
  111. */
  112. protected $encryption = false;
  113. /**
  114. * @internal
  115. *
  116. * @var array
  117. */
  118. protected $encryptedTables = [];
  119. /**
  120. * @internal
  121. *
  122. * @var bool
  123. */
  124. public $allowInherit = false;
  125. /**
  126. * @internal
  127. *
  128. * @var bool
  129. */
  130. public $allowVariants = false;
  131. /**
  132. * @internal
  133. *
  134. * @var bool
  135. */
  136. public $showVariants = false;
  137. /**
  138. * @internal
  139. *
  140. * @var DataObject\ClassDefinition\Data[]
  141. */
  142. public array $fieldDefinitions = [];
  143. /**
  144. * @internal
  145. *
  146. * @var DataObject\ClassDefinition\Layout|null
  147. */
  148. public $layoutDefinitions;
  149. /**
  150. * @internal
  151. *
  152. * @var string
  153. */
  154. public $icon;
  155. /**
  156. * @internal
  157. *
  158. * @var string
  159. */
  160. public $previewUrl;
  161. /**
  162. * @internal
  163. *
  164. * @var string
  165. */
  166. public $group;
  167. /**
  168. * @internal
  169. *
  170. * @var bool
  171. */
  172. public $showAppLoggerTab = false;
  173. /**
  174. * @internal
  175. *
  176. * @var string
  177. */
  178. public $linkGeneratorReference;
  179. /**
  180. * @internal
  181. *
  182. * @var string|null
  183. */
  184. public $previewGeneratorReference;
  185. /**
  186. * @internal
  187. *
  188. * @var array
  189. */
  190. public $compositeIndices = [];
  191. /**
  192. * @internal
  193. *
  194. * @var bool
  195. */
  196. public $generateTypeDeclarations = true;
  197. /**
  198. * @internal
  199. *
  200. * @var bool
  201. */
  202. public $showFieldLookup = false;
  203. /**
  204. * @internal
  205. *
  206. * @var array
  207. */
  208. public $propertyVisibility = [
  209. 'grid' => [
  210. 'id' => true,
  211. 'path' => true,
  212. 'published' => true,
  213. 'modificationDate' => true,
  214. 'creationDate' => true,
  215. ],
  216. 'search' => [
  217. 'id' => true,
  218. 'path' => true,
  219. 'published' => true,
  220. 'modificationDate' => true,
  221. 'creationDate' => true,
  222. ],
  223. ];
  224. /**
  225. * @internal
  226. *
  227. * @var bool
  228. */
  229. public $enableGridLocking = false;
  230. /**
  231. * @param string $id
  232. * @param bool $force
  233. *
  234. * @return null|ClassDefinition
  235. *
  236. * @throws \Exception
  237. */
  238. public static function getById(string $id, $force = false)
  239. {
  240. $cacheKey = 'class_' . $id;
  241. try {
  242. if ($force) {
  243. throw new \Exception('Forced load');
  244. }
  245. $class = \Pimcore\Cache\Runtime::get($cacheKey);
  246. if (!$class) {
  247. throw new \Exception('Class in registry is null');
  248. }
  249. } catch (\Exception $e) {
  250. try {
  251. $class = new self();
  252. $name = $class->getDao()->getNameById($id);
  253. $definitionFile = $class->getDefinitionFile($name);
  254. $class = @include $definitionFile;
  255. if (!$class instanceof self) {
  256. throw new \Exception('Class definition with name ' . $name . ' or ID ' . $id . ' does not exist');
  257. }
  258. $class->setId($id);
  259. \Pimcore\Cache\Runtime::set($cacheKey, $class);
  260. } catch (\Exception $e) {
  261. Logger::info($e->getMessage());
  262. return null;
  263. }
  264. }
  265. return $class;
  266. }
  267. /**
  268. * @param string $name
  269. *
  270. * @return self|null
  271. *
  272. * @throws \Exception
  273. */
  274. public static function getByName($name)
  275. {
  276. try {
  277. $class = new self();
  278. $id = $class->getDao()->getIdByName($name);
  279. return self::getById($id);
  280. } catch (Model\Exception\NotFoundException $e) {
  281. return null;
  282. }
  283. }
  284. /**
  285. * @param array $values
  286. *
  287. * @return self
  288. */
  289. public static function create($values = [])
  290. {
  291. $class = new self();
  292. $class->setValues($values);
  293. return $class;
  294. }
  295. /**
  296. * @internal
  297. *
  298. * @param string $name
  299. */
  300. public function rename($name)
  301. {
  302. $this->deletePhpClasses();
  303. $this->getDao()->updateClassNameInObjects($name);
  304. $this->setName($name);
  305. $this->save();
  306. }
  307. /**
  308. * @param mixed $data
  309. *
  310. * @internal
  311. */
  312. public static function cleanupForExport(&$data)
  313. {
  314. if (!is_object($data)) {
  315. return;
  316. }
  317. if ($data instanceof DataObject\ClassDefinition\Data\VarExporterInterface) {
  318. $blockedVars = $data->resolveBlockedVars();
  319. foreach ($blockedVars as $blockedVar) {
  320. if (isset($data->{$blockedVar})) {
  321. unset($data->{$blockedVar});
  322. }
  323. }
  324. if (isset($data->blockedVarsForExport)) {
  325. unset($data->blockedVarsForExport);
  326. }
  327. }
  328. if (method_exists($data, 'getChildren')) {
  329. $children = $data->getChildren();
  330. if (is_array($children)) {
  331. foreach ($children as $child) {
  332. self::cleanupForExport($child);
  333. }
  334. }
  335. }
  336. }
  337. /**
  338. * @return bool
  339. */
  340. private function exists()
  341. {
  342. $name = $this->getDao()->getNameById($this->getId());
  343. return is_string($name);
  344. }
  345. /**
  346. * @param bool $saveDefinitionFile
  347. *
  348. * @throws \Exception
  349. */
  350. public function save($saveDefinitionFile = true)
  351. {
  352. $fieldDefinitions = $this->getFieldDefinitions();
  353. foreach ($fieldDefinitions as $fd) {
  354. if ($fd->isForbiddenName()) {
  355. throw new \Exception(sprintf('Forbidden name used for field definition: %s', $fd->getName()));
  356. }
  357. if ($fd instanceof DataObject\ClassDefinition\Data\DataContainerAwareInterface) {
  358. $fd->preSave($this);
  359. }
  360. }
  361. if (!$this->getId()) {
  362. $db = Db::get();
  363. $maxId = $db->fetchOne('SELECT MAX(CAST(id AS SIGNED)) FROM classes;');
  364. $this->setId($maxId ? $maxId + 1 : 1);
  365. }
  366. if (!preg_match('/[a-zA-Z][a-zA-Z0-9_]+/', $this->getName())) {
  367. throw new \Exception(sprintf('Invalid name for class definition: %s', $this->getName()));
  368. }
  369. if (!preg_match('/[a-zA-Z0-9]([a-zA-Z0-9_]+)?/', $this->getId())) {
  370. throw new \Exception(sprintf('Invalid ID `%s` for class definition %s', $this->getId(), $this->getName()));
  371. }
  372. foreach (['parentClass', 'listingParentClass', 'useTraits', 'listingUseTraits'] as $propertyName) {
  373. $propertyValue = $this->{'get'.ucfirst($propertyName)}();
  374. if ($propertyValue && !preg_match('/^[a-zA-Z_\x7f-\xff\\\][a-zA-Z0-9_\x7f-\xff\\\ ,]*$/', $propertyValue)) {
  375. throw new \Exception(sprintf('Invalid %s value for class definition: %s', $propertyName,
  376. $this->getParentClass()));
  377. }
  378. }
  379. $isUpdate = $this->exists();
  380. if ($isUpdate && !$this->isWritable()) {
  381. throw new \Exception('definitions in config/pimcore folder cannot be overwritten');
  382. }
  383. if (!$isUpdate) {
  384. \Pimcore::getEventDispatcher()->dispatch(new ClassDefinitionEvent($this), DataObjectClassDefinitionEvents::PRE_ADD);
  385. } else {
  386. \Pimcore::getEventDispatcher()->dispatch(new ClassDefinitionEvent($this), DataObjectClassDefinitionEvents::PRE_UPDATE);
  387. }
  388. $this->setModificationDate(time());
  389. $this->getDao()->save($isUpdate);
  390. $this->generateClassFiles($saveDefinitionFile);
  391. // empty object cache
  392. try {
  393. Cache::clearTag('class_'.$this->getId());
  394. } catch (\Exception $e) {
  395. }
  396. foreach ($fieldDefinitions as $fd) {
  397. if ($fd instanceof DataObject\ClassDefinition\Data\DataContainerAwareInterface) {
  398. $fd->postSave($this);
  399. }
  400. }
  401. if ($isUpdate) {
  402. \Pimcore::getEventDispatcher()->dispatch(new ClassDefinitionEvent($this), DataObjectClassDefinitionEvents::POST_UPDATE);
  403. } else {
  404. \Pimcore::getEventDispatcher()->dispatch(new ClassDefinitionEvent($this), DataObjectClassDefinitionEvents::POST_ADD);
  405. }
  406. }
  407. /**
  408. * @param bool $generateDefinitionFile
  409. *
  410. * @throws \Exception
  411. *
  412. * @internal
  413. */
  414. public function generateClassFiles($generateDefinitionFile = true)
  415. {
  416. $infoDocBlock = $this->getInfoDocBlock();
  417. // create class for object
  418. $extendClass = 'Concrete';
  419. if ($this->getParentClass()) {
  420. $extendClass = $this->getParentClass();
  421. $extendClass = '\\'.ltrim($extendClass, '\\');
  422. }
  423. $cd = '<?php';
  424. $cd .= "\n\n";
  425. $cd .= $infoDocBlock;
  426. $cd .= "\n\n";
  427. $cd .= 'namespace Pimcore\\Model\\DataObject;';
  428. $cd .= "\n\n";
  429. $cd .= 'use Pimcore\Model\DataObject\Exception\InheritanceParentNotFoundException;';
  430. $cd .= "\n";
  431. $cd .= 'use Pimcore\Model\DataObject\PreGetValueHookInterface;';
  432. $cd .= "\n\n";
  433. $cd .= "/**\n";
  434. $cd .= '* @method static \\Pimcore\\Model\\DataObject\\'.ucfirst($this->getName()).'\Listing getList()'."\n";
  435. if (is_array($this->getFieldDefinitions()) && count($this->getFieldDefinitions())) {
  436. foreach ($this->getFieldDefinitions() as $key => $def) {
  437. if ($def instanceof DataObject\ClassDefinition\Data\Localizedfields) {
  438. $cd .= '* @method static \\Pimcore\\Model\\DataObject\\'.ucfirst(
  439. $this->getName()
  440. ).'\Listing|\\Pimcore\\Model\\DataObject\\'.ucfirst(
  441. $this->getName()
  442. ).'|null getBy'.ucfirst(
  443. $def->getName()
  444. ).'($field, $value, $locale = null, $limit = 0, $offset = 0, $objectTypes = null)'."\n";
  445. foreach ($def->getFieldDefinitions() as $localizedFieldDefinition) {
  446. $cd .= '* @method static \\Pimcore\\Model\\DataObject\\'.ucfirst(
  447. $this->getName()
  448. ).'\Listing|\\Pimcore\\Model\\DataObject\\'.ucfirst(
  449. $this->getName()
  450. ).'|null getBy'.ucfirst(
  451. $localizedFieldDefinition->getName()
  452. ).'($value, $locale = null, $limit = 0, $offset = 0, $objectTypes = null)'."\n";
  453. }
  454. } elseif ($def->isFilterable()) {
  455. $cd .= '* @method static \\Pimcore\\Model\\DataObject\\'.ucfirst(
  456. $this->getName()
  457. ).'\Listing|\\Pimcore\\Model\\DataObject\\'.ucfirst(
  458. $this->getName()
  459. ).'|null getBy'.ucfirst($def->getName()).'($value, $limit = 0, $offset = 0, $objectTypes = null)'."\n";
  460. }
  461. }
  462. }
  463. $cd .= "*/\n\n";
  464. $implementsParts = [];
  465. $implements = DataObject\ClassDefinition\Service::buildImplementsInterfacesCode($implementsParts, $this->getImplementsInterfaces());
  466. $cd .= 'class '.ucfirst($this->getName()).' extends '.$extendClass. $implements . "\n";
  467. $cd .= '{' . "\n";
  468. $useParts = [];
  469. $cd .= DataObject\ClassDefinition\Service::buildUseTraitsCode($useParts, $this->getUseTraits());
  470. $cd .= 'protected $o_classId = "' . $this->getId(). "\";\n";
  471. $cd .= 'protected $o_className = "'.$this->getName().'"'.";\n";
  472. if (is_array($this->getFieldDefinitions()) && count($this->getFieldDefinitions())) {
  473. foreach ($this->getFieldDefinitions() as $key => $def) {
  474. if (!$def instanceof DataObject\ClassDefinition\Data\ReverseObjectRelation && !$def instanceof DataObject\ClassDefinition\Data\CalculatedValue
  475. ) {
  476. $cd .= 'protected $'.$key.";\n";
  477. }
  478. }
  479. }
  480. $cd .= "\n\n";
  481. $cd .= '/**'."\n";
  482. $cd .= '* @param array $values'."\n";
  483. $cd .= '* @return \\Pimcore\\Model\\DataObject\\'.ucfirst($this->getName())."\n";
  484. $cd .= '*/'."\n";
  485. $cd .= 'public static function create($values = array()) {';
  486. $cd .= "\n";
  487. $cd .= "\t".'$object = new static();'."\n";
  488. $cd .= "\t".'$object->setValues($values);'."\n";
  489. $cd .= "\t".'return $object;'."\n";
  490. $cd .= '}';
  491. $cd .= "\n\n";
  492. if (is_array($this->getFieldDefinitions()) && count($this->getFieldDefinitions())) {
  493. foreach ($this->getFieldDefinitions() as $key => $def) {
  494. if ($def instanceof DataObject\ClassDefinition\Data\ReverseObjectRelation) {
  495. continue;
  496. }
  497. // get setter and getter code
  498. $cd .= $def->getGetterCode($this);
  499. $cd .= $def->getSetterCode($this);
  500. // call the method "classSaved" if exists, this is used to create additional data tables or whatever which depends on the field definition, for example for localizedfields
  501. //TODO Pimcore 11 remove method_exists call
  502. if (!$def instanceof DataObject\ClassDefinition\Data\DataContainerAwareInterface && method_exists($def, 'classSaved')) {
  503. $def->classSaved($this);
  504. }
  505. }
  506. }
  507. $cd .= "}\n";
  508. $cd .= "\n";
  509. if (File::put($this->getPhpClassFile(), $cd) === false) {
  510. throw new \Exception(sprintf('Cannot write class file in %s please check the rights on this directory', $this->getPhpClassFile()));
  511. }
  512. // create class for object list
  513. $extendListingClass = 'DataObject\\Listing\\Concrete';
  514. if ($this->getListingParentClass()) {
  515. $extendListingClass = $this->getListingParentClass();
  516. $extendListingClass = '\\'.ltrim($extendListingClass, '\\');
  517. }
  518. // create list class
  519. $cd = '<?php';
  520. $cd .= "\n\n";
  521. $cd .= 'namespace Pimcore\\Model\\DataObject\\'.ucfirst($this->getName()).';';
  522. $cd .= "\n\n";
  523. $cd .= 'use Pimcore\\Model\\DataObject;';
  524. $cd .= "\n\n";
  525. $cd .= "/**\n";
  526. $cd .= ' * @method DataObject\\'.ucfirst($this->getName())." current()\n";
  527. $cd .= ' * @method DataObject\\'.ucfirst($this->getName())."[] load()\n";
  528. $cd .= ' * @method DataObject\\'.ucfirst($this->getName())."[] getData()\n";
  529. $cd .= ' */';
  530. $cd .= "\n\n";
  531. $cd .= 'class Listing extends '.$extendListingClass . "\n";
  532. $cd .= '{' . "\n";
  533. $cd .= DataObject\ClassDefinition\Service::buildUseTraitsCode([], $this->getListingUseTraits());
  534. $cd .= 'protected $classId = "'. $this->getId()."\";\n";
  535. $cd .= 'protected $className = "'.$this->getName().'"'.";\n";
  536. $cd .= "\n\n";
  537. if (\is_array($this->getFieldDefinitions())) {
  538. foreach ($this->getFieldDefinitions() as $key => $def) {
  539. if ($def instanceof DataObject\ClassDefinition\Data\Localizedfields) {
  540. foreach ($def->getFieldDefinitions() as $localizedFieldDefinition) {
  541. $cd .= $localizedFieldDefinition->getFilterCode();
  542. }
  543. } elseif ($def->isFilterable()) {
  544. $cd .= $def->getFilterCode();
  545. }
  546. }
  547. }
  548. $cd .= "\n\n";
  549. $cd .= "}\n";
  550. if (File::put($this->getPhpListingClassFile(), $cd) === false) {
  551. throw new \Exception(
  552. sprintf('Cannot write class file in %s please check the rights on this directory', $this->getPhpListingClassFile())
  553. );
  554. }
  555. if ($generateDefinitionFile) {
  556. // save definition as a php file
  557. $definitionFile = $this->getDefinitionFile();
  558. if (!is_writable(dirname($definitionFile)) || (is_file($definitionFile) && !is_writable($definitionFile))) {
  559. throw new \Exception(
  560. 'Cannot write definition file in: '.$definitionFile.' please check write permission on this directory.'
  561. );
  562. }
  563. /** @var self $clone */
  564. $clone = DataObject\Service::cloneDefinition($this);
  565. $clone->setDao(null);
  566. $clone->fieldDefinitions = [];
  567. self::cleanupForExport($clone->layoutDefinitions);
  568. $exportedClass = var_export($clone, true);
  569. $data = '<?php';
  570. $data .= "\n\n";
  571. $data .= $infoDocBlock;
  572. $data .= "\n\n";
  573. $data .= "\nreturn ".$exportedClass.";\n";
  574. \Pimcore\File::putPhpFile($definitionFile, $data);
  575. }
  576. }
  577. /**
  578. * @return string
  579. *
  580. * @internal
  581. */
  582. protected function getInfoDocBlock()
  583. {
  584. $cd = '/**' . "\n";
  585. $cd .= '* Inheritance: '.($this->getAllowInherit() ? 'yes' : 'no')."\n";
  586. $cd .= '* Variants: '.($this->getAllowVariants() ? 'yes' : 'no')."\n";
  587. if ($this->getDescription()) {
  588. $description = str_replace(['/**', '*/', '//'], '', $this->getDescription());
  589. $description = str_replace("\n", "\n* ", $description);
  590. $cd .= '* '.$description."\n";
  591. }
  592. $cd .= "\n\n";
  593. $cd .= "Fields Summary:\n";
  594. $cd = $this->getInfoDocBlockForFields($this, $cd, 1);
  595. $cd .= '*/';
  596. return $cd;
  597. }
  598. /**
  599. * @internal
  600. *
  601. * @param ClassDefinition|ClassDefinition\Data $definition
  602. * @param string $text
  603. * @param int $level
  604. *
  605. * @return string
  606. */
  607. protected function getInfoDocBlockForFields($definition, $text, $level)
  608. {
  609. foreach ($definition->getFieldDefinitions() as $fd) {
  610. $text .= str_pad('', $level, '-').' '.$fd->getName().' ['.$fd->getFieldtype()."]\n";
  611. if (method_exists($fd, 'getFieldDefinitions')) {
  612. $text = $this->getInfoDocBlockForFields($fd, $text, $level + 1);
  613. }
  614. }
  615. return $text;
  616. }
  617. public function delete()
  618. {
  619. \Pimcore::getEventDispatcher()->dispatch(new ClassDefinitionEvent($this), DataObjectClassDefinitionEvents::PRE_DELETE);
  620. // delete all objects using this class
  621. $list = new Listing();
  622. $list->setCondition('o_classId = ?', $this->getId());
  623. $list->load();
  624. foreach ($list->getObjects() as $o) {
  625. $o->delete();
  626. }
  627. $this->deletePhpClasses();
  628. // empty object cache
  629. try {
  630. Cache::clearTag('class_'.$this->getId());
  631. } catch (\Exception $e) {
  632. }
  633. // empty output cache
  634. try {
  635. Cache::clearTag('output');
  636. } catch (\Exception $e) {
  637. }
  638. $customLayouts = new ClassDefinition\CustomLayout\Listing();
  639. $customLayouts->setCondition('classId = ?', $this->getId());
  640. $customLayouts = $customLayouts->load();
  641. foreach ($customLayouts as $customLayout) {
  642. $customLayout->delete();
  643. }
  644. $brickListing = new DataObject\Objectbrick\Definition\Listing();
  645. $brickListing = $brickListing->load();
  646. foreach ($brickListing as $brickDefinition) {
  647. $modified = false;
  648. $classDefinitions = $brickDefinition->getClassDefinitions();
  649. if (is_array($classDefinitions)) {
  650. foreach ($classDefinitions as $key => $classDefinition) {
  651. if ($classDefinition['classname'] == $this->getId()) {
  652. unset($classDefinitions[$key]);
  653. $modified = true;
  654. }
  655. }
  656. }
  657. if ($modified) {
  658. $brickDefinition->setClassDefinitions($classDefinitions);
  659. $brickDefinition->save();
  660. }
  661. }
  662. $this->getDao()->delete();
  663. \Pimcore::getEventDispatcher()->dispatch(new ClassDefinitionEvent($this), DataObjectClassDefinitionEvents::POST_DELETE);
  664. }
  665. private function deletePhpClasses()
  666. {
  667. // delete the class files
  668. @unlink($this->getPhpClassFile());
  669. @unlink($this->getPhpListingClassFile());
  670. @rmdir(dirname($this->getPhpListingClassFile()));
  671. @unlink($this->getDefinitionFile());
  672. }
  673. /**
  674. * @internal
  675. *
  676. * @return bool
  677. */
  678. public function isWritable(): bool
  679. {
  680. if (getenv('PIMCORE_CLASS_DEFINITION_WRITABLE')) {
  681. return true;
  682. }
  683. return !str_starts_with($this->getDefinitionFile(), PIMCORE_CUSTOM_CONFIGURATION_DIRECTORY);
  684. }
  685. /**
  686. * @internal
  687. *
  688. * @param string|null $name
  689. *
  690. * @return string
  691. */
  692. public function getDefinitionFile($name = null)
  693. {
  694. return $this->locateFile($name ?? $this->getName(), 'definition_%s.php');
  695. }
  696. private function getPhpClassFile(): string
  697. {
  698. return $this->locateFile(ucfirst($this->getName()), 'DataObject/%s.php');
  699. }
  700. private function getPhpListingClassFile(): string
  701. {
  702. return $this->locateFile(ucfirst($this->getName()), 'DataObject/%s/Listing.php');
  703. }
  704. /**
  705. * @return string|null
  706. */
  707. public function getId()
  708. {
  709. return $this->id;
  710. }
  711. /**
  712. * @return string|null
  713. */
  714. public function getName()
  715. {
  716. return $this->name;
  717. }
  718. /**
  719. * @return int
  720. */
  721. public function getCreationDate()
  722. {
  723. return $this->creationDate;
  724. }
  725. /**
  726. * @return int
  727. */
  728. public function getModificationDate()
  729. {
  730. return $this->modificationDate;
  731. }
  732. /**
  733. * @return int
  734. */
  735. public function getUserOwner()
  736. {
  737. return $this->userOwner;
  738. }
  739. /**
  740. * @return int
  741. */
  742. public function getUserModification()
  743. {
  744. return $this->userModification;
  745. }
  746. /**
  747. * @param string $id
  748. *
  749. * @return $this
  750. */
  751. public function setId($id)
  752. {
  753. $this->id = $id;
  754. return $this;
  755. }
  756. /**
  757. * @param string $name
  758. *
  759. * @return $this
  760. */
  761. public function setName($name)
  762. {
  763. $this->name = $name;
  764. return $this;
  765. }
  766. /**
  767. * @param int $creationDate
  768. *
  769. * @return $this
  770. */
  771. public function setCreationDate($creationDate)
  772. {
  773. $this->creationDate = (int)$creationDate;
  774. return $this;
  775. }
  776. /**
  777. * @param int $modificationDate
  778. *
  779. * @return $this
  780. */
  781. public function setModificationDate($modificationDate)
  782. {
  783. $this->modificationDate = (int)$modificationDate;
  784. return $this;
  785. }
  786. /**
  787. * @param int $userOwner
  788. *
  789. * @return $this
  790. */
  791. public function setUserOwner($userOwner)
  792. {
  793. $this->userOwner = (int)$userOwner;
  794. return $this;
  795. }
  796. /**
  797. * @param int $userModification
  798. *
  799. * @return $this
  800. */
  801. public function setUserModification($userModification)
  802. {
  803. $this->userModification = (int)$userModification;
  804. return $this;
  805. }
  806. /**
  807. * @param array $context
  808. *
  809. * @return DataObject\ClassDefinition\Data[]
  810. */
  811. public function getFieldDefinitions($context = [])
  812. {
  813. if (!\Pimcore::inAdmin() || (isset($context['suppressEnrichment']) && $context['suppressEnrichment'])) {
  814. return $this->fieldDefinitions;
  815. }
  816. $enrichedFieldDefinitions = [];
  817. foreach ($this->fieldDefinitions as $key => $fieldDefinition) {
  818. $fieldDefinition = $this->doEnrichFieldDefinition($fieldDefinition, $context);
  819. $enrichedFieldDefinitions[$key] = $fieldDefinition;
  820. }
  821. return $enrichedFieldDefinitions;
  822. }
  823. /**
  824. * @internal
  825. */
  826. protected function doEnrichFieldDefinition($fieldDefinition, $context = [])
  827. {
  828. //TODO Pimcore 11: remove method_exists BC layer
  829. if ($fieldDefinition instanceof FieldDefinitionEnrichmentInterface || method_exists($fieldDefinition, 'enrichFieldDefinition')) {
  830. if (!$fieldDefinition instanceof FieldDefinitionEnrichmentInterface) {
  831. trigger_deprecation('pimcore/pimcore', '10.1',
  832. sprintf('Usage of method_exists is deprecated since version 10.1 and will be removed in Pimcore 11.' .
  833. 'Implement the %s interface instead.', FieldDefinitionEnrichmentInterface::class));
  834. }
  835. $context['class'] = $this;
  836. $fieldDefinition = $fieldDefinition->enrichFieldDefinition($context);
  837. }
  838. return $fieldDefinition;
  839. }
  840. /**
  841. * @return DataObject\ClassDefinition\Layout|null
  842. */
  843. public function getLayoutDefinitions()
  844. {
  845. return $this->layoutDefinitions;
  846. }
  847. /**
  848. * @param DataObject\ClassDefinition\Data[] $fieldDefinitions
  849. *
  850. * @return $this
  851. */
  852. public function setFieldDefinitions(array $fieldDefinitions)
  853. {
  854. $this->fieldDefinitions = $fieldDefinitions;
  855. return $this;
  856. }
  857. /**
  858. * @param string $key
  859. * @param DataObject\ClassDefinition\Data $data
  860. *
  861. * @return $this
  862. */
  863. public function addFieldDefinition($key, $data)
  864. {
  865. $this->fieldDefinitions[$key] = $data;
  866. return $this;
  867. }
  868. /**
  869. * @param string $key
  870. * @param array $context
  871. *
  872. * @return DataObject\ClassDefinition\Data|null
  873. */
  874. public function getFieldDefinition($key, $context = [])
  875. {
  876. if (array_key_exists($key, $this->fieldDefinitions)) {
  877. if (!\Pimcore::inAdmin() || (isset($context['suppressEnrichment']) && $context['suppressEnrichment'])) {
  878. return $this->fieldDefinitions[$key];
  879. }
  880. $fieldDefinition = $this->doEnrichFieldDefinition($this->fieldDefinitions[$key], $context);
  881. return $fieldDefinition;
  882. }
  883. return null;
  884. }
  885. /**
  886. * @param DataObject\ClassDefinition\Layout|null $layoutDefinitions
  887. *
  888. * @return $this
  889. */
  890. public function setLayoutDefinitions($layoutDefinitions)
  891. {
  892. $this->layoutDefinitions = $layoutDefinitions;
  893. $this->fieldDefinitions = [];
  894. $this->extractDataDefinitions($this->layoutDefinitions);
  895. return $this;
  896. }
  897. /**
  898. * @param DataObject\ClassDefinition\Layout|DataObject\ClassDefinition\Data $def
  899. */
  900. private function extractDataDefinitions($def)
  901. {
  902. if ($def instanceof DataObject\ClassDefinition\Layout) {
  903. if ($def->hasChildren()) {
  904. foreach ($def->getChildren() as $child) {
  905. $this->extractDataDefinitions($child);
  906. }
  907. }
  908. }
  909. if ($def instanceof DataObject\ClassDefinition\Data) {
  910. $existing = $this->getFieldDefinition($def->getName());
  911. if (!$existing && method_exists($def, 'addReferencedField') && method_exists($def, 'setReferencedFields')) {
  912. $def->setReferencedFields([]);
  913. }
  914. if ($existing && method_exists($existing, 'addReferencedField')) {
  915. // this is especially for localized fields which get aggregated here into one field definition
  916. // in the case that there are more than one localized fields in the class definition
  917. // see also pimcore.object.edit.addToDataFields();
  918. $existing->addReferencedField($def);
  919. } else {
  920. $this->addFieldDefinition($def->getName(), $def);
  921. }
  922. }
  923. }
  924. /**
  925. * @return string
  926. */
  927. public function getParentClass()
  928. {
  929. return $this->parentClass;
  930. }
  931. /**
  932. * @return string
  933. */
  934. public function getListingParentClass()
  935. {
  936. return $this->listingParentClass;
  937. }
  938. /**
  939. * @return string
  940. */
  941. public function getUseTraits()
  942. {
  943. return $this->useTraits;
  944. }
  945. /**
  946. * @param string $useTraits
  947. *
  948. * @return ClassDefinition
  949. */
  950. public function setUseTraits($useTraits)
  951. {
  952. $this->useTraits = (string) $useTraits;
  953. return $this;
  954. }
  955. /**
  956. * @return string
  957. */
  958. public function getListingUseTraits()
  959. {
  960. return $this->listingUseTraits;
  961. }
  962. /**
  963. * @param string $listingUseTraits
  964. *
  965. * @return ClassDefinition
  966. */
  967. public function setListingUseTraits($listingUseTraits)
  968. {
  969. $this->listingUseTraits = (string) $listingUseTraits;
  970. return $this;
  971. }
  972. /**
  973. * @return bool
  974. */
  975. public function getAllowInherit()
  976. {
  977. return $this->allowInherit;
  978. }
  979. /**
  980. * @return bool
  981. */
  982. public function getAllowVariants()
  983. {
  984. return $this->allowVariants;
  985. }
  986. /**
  987. * @param string $parentClass
  988. *
  989. * @return $this
  990. */
  991. public function setParentClass($parentClass)
  992. {
  993. $this->parentClass = $parentClass;
  994. return $this;
  995. }
  996. /**
  997. * @param string $listingParentClass
  998. *
  999. * @return $this
  1000. */
  1001. public function setListingParentClass($listingParentClass)
  1002. {
  1003. $this->listingParentClass = (string) $listingParentClass;
  1004. return $this;
  1005. }
  1006. /**
  1007. * @return bool
  1008. */
  1009. public function getEncryption(): bool
  1010. {
  1011. return $this->encryption;
  1012. }
  1013. /**
  1014. * @param bool $encryption
  1015. *
  1016. * @return $this
  1017. */
  1018. public function setEncryption(bool $encryption)
  1019. {
  1020. $this->encryption = $encryption;
  1021. return $this;
  1022. }
  1023. /**
  1024. * @internal
  1025. *
  1026. * @param array $tables
  1027. */
  1028. public function addEncryptedTables(array $tables)
  1029. {
  1030. $this->encryptedTables = array_unique(array_merge($this->encryptedTables, $tables));
  1031. }
  1032. /**
  1033. * @internal
  1034. *
  1035. * @param array $tables
  1036. */
  1037. public function removeEncryptedTables(array $tables)
  1038. {
  1039. foreach ($tables as $table) {
  1040. if (($key = array_search($table, $this->encryptedTables)) !== false) {
  1041. unset($this->encryptedTables[$key]);
  1042. }
  1043. }
  1044. }
  1045. /**
  1046. * @internal
  1047. *
  1048. * @param string $table
  1049. *
  1050. * @return bool
  1051. */
  1052. public function isEncryptedTable(string $table): bool
  1053. {
  1054. return (array_search($table, $this->encryptedTables) === false) ? false : true;
  1055. }
  1056. /**
  1057. * @return bool
  1058. */
  1059. public function hasEncryptedTables(): bool
  1060. {
  1061. return (bool) count($this->encryptedTables);
  1062. }
  1063. /**
  1064. * @internal
  1065. *
  1066. * @param array $encryptedTables
  1067. *
  1068. * @return $this
  1069. */
  1070. public function setEncryptedTables(array $encryptedTables)
  1071. {
  1072. $this->encryptedTables = $encryptedTables;
  1073. return $this;
  1074. }
  1075. /**
  1076. * @param bool $allowInherit
  1077. *
  1078. * @return $this
  1079. */
  1080. public function setAllowInherit($allowInherit)
  1081. {
  1082. $this->allowInherit = (bool)$allowInherit;
  1083. return $this;
  1084. }
  1085. /**
  1086. * @param bool $allowVariants
  1087. *
  1088. * @return $this
  1089. */
  1090. public function setAllowVariants($allowVariants)
  1091. {
  1092. $this->allowVariants = (bool)$allowVariants;
  1093. return $this;
  1094. }
  1095. /**
  1096. * @return string
  1097. */
  1098. public function getIcon()
  1099. {
  1100. return $this->icon;
  1101. }
  1102. /**
  1103. * @param string $icon
  1104. *
  1105. * @return $this
  1106. */
  1107. public function setIcon($icon)
  1108. {
  1109. $this->icon = $icon;
  1110. return $this;
  1111. }
  1112. /**
  1113. * @return array
  1114. */
  1115. public function getPropertyVisibility()
  1116. {
  1117. return $this->propertyVisibility;
  1118. }
  1119. /**
  1120. * @param array $propertyVisibility
  1121. *
  1122. * @return $this
  1123. */
  1124. public function setPropertyVisibility($propertyVisibility)
  1125. {
  1126. if (is_array($propertyVisibility)) {
  1127. $this->propertyVisibility = $propertyVisibility;
  1128. }
  1129. return $this;
  1130. }
  1131. /**
  1132. * @param string $previewUrl
  1133. *
  1134. * @return $this
  1135. */
  1136. public function setPreviewUrl($previewUrl)
  1137. {
  1138. $this->previewUrl = $previewUrl;
  1139. return $this;
  1140. }
  1141. /**
  1142. * @return string
  1143. */
  1144. public function getPreviewUrl()
  1145. {
  1146. return $this->previewUrl;
  1147. }
  1148. /**
  1149. * @return string
  1150. */
  1151. public function getGroup()
  1152. {
  1153. return $this->group;
  1154. }
  1155. /**
  1156. * @param string $group
  1157. *
  1158. * @return $this
  1159. */
  1160. public function setGroup($group)
  1161. {
  1162. $this->group = $group;
  1163. return $this;
  1164. }
  1165. /**
  1166. * @param string $description
  1167. *
  1168. * @return $this
  1169. */
  1170. public function setDescription($description)
  1171. {
  1172. $this->description = $description;
  1173. return $this;
  1174. }
  1175. /**
  1176. * @return string
  1177. */
  1178. public function getDescription()
  1179. {
  1180. return $this->description;
  1181. }
  1182. /**
  1183. * @param bool $showVariants
  1184. *
  1185. * @return $this
  1186. */
  1187. public function setShowVariants($showVariants)
  1188. {
  1189. $this->showVariants = (bool)$showVariants;
  1190. return $this;
  1191. }
  1192. /**
  1193. * @return bool
  1194. */
  1195. public function getShowVariants()
  1196. {
  1197. return $this->showVariants;
  1198. }
  1199. /**
  1200. * @return bool
  1201. */
  1202. public function getShowAppLoggerTab()
  1203. {
  1204. return $this->showAppLoggerTab;
  1205. }
  1206. /**
  1207. * @param bool $showAppLoggerTab
  1208. *
  1209. * @return $this
  1210. */
  1211. public function setShowAppLoggerTab($showAppLoggerTab)
  1212. {
  1213. $this->showAppLoggerTab = (bool) $showAppLoggerTab;
  1214. return $this;
  1215. }
  1216. /**
  1217. * @return bool
  1218. */
  1219. public function getShowFieldLookup()
  1220. {
  1221. return $this->showFieldLookup;
  1222. }
  1223. /**
  1224. * @param bool $showFieldLookup
  1225. *
  1226. * @return $this
  1227. */
  1228. public function setShowFieldLookup($showFieldLookup)
  1229. {
  1230. $this->showFieldLookup = (bool) $showFieldLookup;
  1231. return $this;
  1232. }
  1233. /**
  1234. * @return string
  1235. */
  1236. public function getLinkGeneratorReference()
  1237. {
  1238. return $this->linkGeneratorReference;
  1239. }
  1240. /**
  1241. * @param string $linkGeneratorReference
  1242. *
  1243. * @return $this
  1244. */
  1245. public function setLinkGeneratorReference($linkGeneratorReference)
  1246. {
  1247. $this->linkGeneratorReference = $linkGeneratorReference;
  1248. return $this;
  1249. }
  1250. /**
  1251. * @return DataObject\ClassDefinition\LinkGeneratorInterface|null
  1252. */
  1253. public function getLinkGenerator()
  1254. {
  1255. return DataObject\ClassDefinition\Helper\LinkGeneratorResolver::resolveGenerator($this->getLinkGeneratorReference());
  1256. }
  1257. /**
  1258. * @return string|null
  1259. */
  1260. public function getPreviewGeneratorReference(): ?string
  1261. {
  1262. return $this->previewGeneratorReference;
  1263. }
  1264. /**
  1265. * @param string|null $previewGeneratorReference
  1266. */
  1267. public function setPreviewGeneratorReference(?string $previewGeneratorReference): void
  1268. {
  1269. $this->previewGeneratorReference = $previewGeneratorReference;
  1270. }
  1271. /**
  1272. * @return DataObject\ClassDefinition\PreviewGeneratorInterface|null
  1273. */
  1274. public function getPreviewGenerator()
  1275. {
  1276. return DataObject\ClassDefinition\Helper\PreviewGeneratorResolver::resolveGenerator($this->getPreviewGeneratorReference());
  1277. }
  1278. /**
  1279. * @return bool
  1280. */
  1281. public function isEnableGridLocking(): bool
  1282. {
  1283. return $this->enableGridLocking;
  1284. }
  1285. /**
  1286. * @param bool $enableGridLocking
  1287. */
  1288. public function setEnableGridLocking(bool $enableGridLocking): void
  1289. {
  1290. $this->enableGridLocking = $enableGridLocking;
  1291. }
  1292. /**
  1293. * @return string|null
  1294. */
  1295. public function getImplementsInterfaces(): ?string
  1296. {
  1297. return $this->implementsInterfaces;
  1298. }
  1299. /**
  1300. * @param string|null $implementsInterfaces
  1301. *
  1302. * @return $this
  1303. */
  1304. public function setImplementsInterfaces(?string $implementsInterfaces)
  1305. {
  1306. $this->implementsInterfaces = $implementsInterfaces;
  1307. return $this;
  1308. }
  1309. /**
  1310. * @return array
  1311. */
  1312. public function getCompositeIndices(): array
  1313. {
  1314. return $this->compositeIndices;
  1315. }
  1316. /**
  1317. * @param array $compositeIndices
  1318. *
  1319. * @return $this
  1320. */
  1321. public function setCompositeIndices($compositeIndices)
  1322. {
  1323. $this->compositeIndices = $compositeIndices ?? [];
  1324. return $this;
  1325. }
  1326. /**
  1327. * @return bool
  1328. */
  1329. public function getGenerateTypeDeclarations()
  1330. {
  1331. return (bool) $this->generateTypeDeclarations;
  1332. }
  1333. /**
  1334. * @param bool $generateTypeDeclarations
  1335. *
  1336. * @return $this
  1337. */
  1338. public function setGenerateTypeDeclarations($generateTypeDeclarations)
  1339. {
  1340. $this->generateTypeDeclarations = (bool) $generateTypeDeclarations;
  1341. return $this;
  1342. }
  1343. }