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

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