vendor/pimcore/pimcore/models/DataObject/ClassDefinition/Data/ManyToOneRelation.php line 251

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\ClassDefinition\Data;
  15. use Pimcore\Logger;
  16. use Pimcore\Model;
  17. use Pimcore\Model\Asset;
  18. use Pimcore\Model\DataObject;
  19. use Pimcore\Model\DataObject\ClassDefinition\Data\Relations\AbstractRelations;
  20. use Pimcore\Model\Document;
  21. use Pimcore\Model\Element;
  22. use Pimcore\Normalizer\NormalizerInterface;
  23. class ManyToOneRelation extends AbstractRelations implements QueryResourcePersistenceAwareInterface, TypeDeclarationSupportInterface, VarExporterInterface, NormalizerInterface, IdRewriterInterface, PreGetDataInterface, PreSetDataInterface
  24. {
  25. use Model\DataObject\ClassDefinition\Data\Extension\Relation;
  26. use Extension\QueryColumnType;
  27. use DataObject\ClassDefinition\Data\Relations\AllowObjectRelationTrait;
  28. use DataObject\ClassDefinition\Data\Relations\AllowAssetRelationTrait;
  29. use DataObject\ClassDefinition\Data\Relations\AllowDocumentRelationTrait;
  30. /**
  31. * Static type of this element
  32. *
  33. * @internal
  34. *
  35. * @var string
  36. */
  37. public $fieldtype = 'manyToOneRelation';
  38. /**
  39. * @internal
  40. *
  41. * @var string|int
  42. */
  43. public $width = 0;
  44. /**
  45. * @internal
  46. *
  47. * @var string
  48. */
  49. public $assetUploadPath;
  50. /**
  51. * @internal
  52. *
  53. * @var bool
  54. */
  55. public $relationType = true;
  56. /**
  57. * Type for the column to query
  58. *
  59. * @internal
  60. *
  61. * @var array
  62. */
  63. public $queryColumnType = [
  64. 'id' => 'int(11)',
  65. 'type' => "enum('document','asset','object')",
  66. ];
  67. /**
  68. * @internal
  69. *
  70. * @var bool
  71. */
  72. public $objectsAllowed = false;
  73. /**
  74. * @internal
  75. *
  76. * @var bool
  77. */
  78. public $assetsAllowed = false;
  79. /**
  80. * Allowed asset types
  81. *
  82. * @internal
  83. *
  84. * @var array
  85. */
  86. public $assetTypes = [];
  87. /**
  88. * @internal
  89. *
  90. * @var bool
  91. */
  92. public $documentsAllowed = false;
  93. /**
  94. * Allowed document types
  95. *
  96. * @internal
  97. *
  98. * @var array
  99. */
  100. public $documentTypes = [];
  101. /**
  102. * @return bool
  103. */
  104. public function getObjectsAllowed()
  105. {
  106. return $this->objectsAllowed;
  107. }
  108. /**
  109. * @param bool $objectsAllowed
  110. *
  111. * @return $this
  112. */
  113. public function setObjectsAllowed($objectsAllowed)
  114. {
  115. $this->objectsAllowed = $objectsAllowed;
  116. return $this;
  117. }
  118. /**
  119. * @return bool
  120. */
  121. public function getDocumentsAllowed()
  122. {
  123. return $this->documentsAllowed;
  124. }
  125. /**
  126. * @param bool $documentsAllowed
  127. *
  128. * @return $this
  129. */
  130. public function setDocumentsAllowed($documentsAllowed)
  131. {
  132. $this->documentsAllowed = $documentsAllowed;
  133. return $this;
  134. }
  135. /**
  136. * @return array
  137. */
  138. public function getDocumentTypes()
  139. {
  140. return $this->documentTypes ?: [];
  141. }
  142. /**
  143. * @param array $documentTypes
  144. *
  145. * @return $this
  146. */
  147. public function setDocumentTypes($documentTypes)
  148. {
  149. $this->documentTypes = Element\Service::fixAllowedTypes($documentTypes, 'documentTypes');
  150. return $this;
  151. }
  152. /**
  153. *
  154. * @return bool
  155. */
  156. public function getAssetsAllowed()
  157. {
  158. return $this->assetsAllowed;
  159. }
  160. /**
  161. *
  162. * @param bool $assetsAllowed
  163. *
  164. * @return $this
  165. */
  166. public function setAssetsAllowed($assetsAllowed)
  167. {
  168. $this->assetsAllowed = $assetsAllowed;
  169. return $this;
  170. }
  171. /**
  172. * @return array
  173. */
  174. public function getAssetTypes()
  175. {
  176. return $this->assetTypes ?: [];
  177. }
  178. /**
  179. * @param array $assetTypes
  180. *
  181. * @return $this
  182. */
  183. public function setAssetTypes($assetTypes)
  184. {
  185. $this->assetTypes = Element\Service::fixAllowedTypes($assetTypes, 'assetTypes');
  186. return $this;
  187. }
  188. /**
  189. * {@inheritdoc}
  190. */
  191. protected function prepareDataForPersistence($data, $object = null, $params = [])
  192. {
  193. if ($data instanceof Element\ElementInterface) {
  194. $type = Element\Service::getType($data);
  195. $id = $data->getId();
  196. return [[
  197. 'dest_id' => $id,
  198. 'type' => $type,
  199. 'fieldname' => $this->getName(),
  200. ]];
  201. }
  202. return null;
  203. }
  204. /**
  205. * {@inheritdoc}
  206. */
  207. protected function loadData(array $data, $object = null, $params = [])
  208. {
  209. // data from relation table
  210. $data = current($data);
  211. $result = [
  212. 'dirty' => false,
  213. 'data' => null,
  214. ];
  215. if (!empty($data['dest_id']) && !empty($data['type'])) {
  216. $element = Element\Service::getElementById($data['type'], $data['dest_id']);
  217. if ($element instanceof Element\ElementInterface) {
  218. $result['data'] = $element;
  219. } else {
  220. $result['dirty'] = true;
  221. }
  222. }
  223. return $result;
  224. }
  225. /**
  226. * @see QueryResourcePersistenceAwareInterface::getDataForQueryResource
  227. *
  228. * @param Asset|Document|DataObject\AbstractObject $data
  229. * @param null|DataObject\Concrete $object
  230. * @param mixed $params
  231. *
  232. * @return array
  233. */
  234. public function getDataForQueryResource($data, $object = null, $params = [])
  235. {
  236. $rData = $this->prepareDataForPersistence($data, $object, $params);
  237. $return = [];
  238. $return[$this->getName() . '__id'] = isset($rData[0]['dest_id']) ? $rData[0]['dest_id'] : null;
  239. $return[$this->getName() . '__type'] = isset($rData[0]['type']) ? $rData[0]['type'] : null;
  240. return $return;
  241. }
  242. /**
  243. * @see Data::getDataForEditmode
  244. *
  245. * @param Element\ElementInterface|null $data
  246. * @param null|DataObject\Concrete $object
  247. * @param array|null $params
  248. *
  249. * @return array|null
  250. */
  251. public function getDataForEditmode($data, $object = null, $params = [])
  252. {
  253. if ($data instanceof Element\ElementInterface) {
  254. $r = [
  255. 'id' => $data->getId(),
  256. 'path' => $data->getRealFullPath(),
  257. 'subtype' => $data->getType(),
  258. 'type' => Element\Service::getElementType($data),
  259. 'published' => Element\Service::isPublished($data),
  260. ];
  261. return $r;
  262. }
  263. return null;
  264. }
  265. /**
  266. * @see Data::getDataFromEditmode
  267. *
  268. * @param array $data
  269. * @param null|DataObject\Concrete $object
  270. * @param mixed $params
  271. *
  272. * @return Asset|Document|DataObject\AbstractObject|null
  273. */
  274. public function getDataFromEditmode($data, $object = null, $params = [])
  275. {
  276. if (!empty($data['id']) && !empty($data['type'])) {
  277. return Element\Service::getElementById($data['type'], $data['id']);
  278. }
  279. return null;
  280. }
  281. /**
  282. * @param array $data
  283. * @param null|DataObject\Concrete $object
  284. * @param mixed $params
  285. *
  286. * @return Asset|Document|DataObject\AbstractObject
  287. */
  288. public function getDataFromGridEditor($data, $object = null, $params = [])
  289. {
  290. return $this->getDataFromEditmode($data, $object, $params);
  291. }
  292. /**
  293. * @param Element\ElementInterface|null $data
  294. * @param DataObject\Concrete $object
  295. * @param array $params
  296. *
  297. * @return array|null
  298. */
  299. public function getDataForGrid($data, $object = null, $params = [])
  300. {
  301. return $this->getDataForEditmode($data, $object, $params);
  302. }
  303. /**
  304. * @see Data::getVersionPreview
  305. *
  306. * @param Element\ElementInterface|null $data
  307. * @param null|DataObject\Concrete $object
  308. * @param mixed $params
  309. *
  310. * @return string
  311. */
  312. public function getVersionPreview($data, $object = null, $params = [])
  313. {
  314. if ($data instanceof Element\ElementInterface) {
  315. return Element\Service::getElementType($data).' '.$data->getRealFullPath();
  316. }
  317. return '';
  318. }
  319. /**
  320. * @return string|int
  321. */
  322. public function getWidth()
  323. {
  324. return $this->width;
  325. }
  326. /**
  327. * @param string|int $width
  328. *
  329. * @return $this
  330. */
  331. public function setWidth($width)
  332. {
  333. if (is_numeric($width)) {
  334. $width = (int)$width;
  335. }
  336. $this->width = $width;
  337. return $this;
  338. }
  339. /**
  340. * {@inheritdoc}
  341. */
  342. public function checkValidity($data, $omitMandatoryCheck = false, $params = [])
  343. {
  344. if (!$omitMandatoryCheck && $this->getMandatory() && empty($data)) {
  345. throw new Element\ValidationException('Empty mandatory field [ '.$this->getName().' ]');
  346. }
  347. if ($data instanceof Document) {
  348. $allow = $this->allowDocumentRelation($data);
  349. } elseif ($data instanceof Asset) {
  350. $allow = $this->allowAssetRelation($data);
  351. } elseif ($data instanceof DataObject\AbstractObject) {
  352. $allow = $this->allowObjectRelation($data);
  353. } elseif (empty($data)) {
  354. $allow = true;
  355. } else {
  356. Logger::error(sprintf('Invalid data in field `%s` [type: %s]', $this->getName(), $this->getFieldtype()));
  357. $allow = false;
  358. }
  359. if (!$allow) {
  360. throw new Element\ValidationException(sprintf('Invalid data in field `%s` [type: %s]', $this->getName(), $this->getFieldtype()), null, null);
  361. }
  362. }
  363. /**
  364. * {@inheritdoc}
  365. */
  366. public function getForCsvExport($object, $params = [])
  367. {
  368. $data = $this->getDataFromObjectParam($object, $params);
  369. if ($data instanceof Element\ElementInterface) {
  370. return Element\Service::getType($data).':'.$data->getRealFullPath();
  371. }
  372. return '';
  373. }
  374. /**
  375. * @param Element\ElementInterface|null $data
  376. *
  377. * @return array
  378. */
  379. public function resolveDependencies($data)
  380. {
  381. $dependencies = [];
  382. if ($data instanceof Element\ElementInterface) {
  383. $elementType = Element\Service::getElementType($data);
  384. $dependencies[$elementType . '_' . $data->getId()] = [
  385. 'id' => $data->getId(),
  386. 'type' => $elementType,
  387. ];
  388. }
  389. return $dependencies;
  390. }
  391. /**
  392. * @param DataObject\Concrete|DataObject\Localizedfield|DataObject\Objectbrick\Data\AbstractData|DataObject\Fieldcollection\Data\AbstractData $container
  393. * @param array $params
  394. *
  395. * @return null|Element\ElementInterface
  396. */
  397. public function preGetData(/** mixed */ $container, /** array */ $params = []) // : mixed
  398. {
  399. $data = null;
  400. if ($container instanceof DataObject\Concrete) {
  401. $data = $container->getObjectVar($this->getName());
  402. if (!$container->isLazyKeyLoaded($this->getName())) {
  403. $data = $this->load($container);
  404. $container->setObjectVar($this->getName(), $data);
  405. $this->markLazyloadedFieldAsLoaded($container);
  406. }
  407. } elseif ($container instanceof DataObject\Localizedfield) {
  408. $data = $params['data'];
  409. } elseif ($container instanceof DataObject\Fieldcollection\Data\AbstractData) {
  410. parent::loadLazyFieldcollectionField($container);
  411. $data = $container->getObjectVar($this->getName());
  412. } elseif ($container instanceof DataObject\Objectbrick\Data\AbstractData) {
  413. parent::loadLazyBrickField($container);
  414. $data = $container->getObjectVar($this->getName());
  415. }
  416. if (DataObject::doHideUnpublished() && ($data instanceof Element\ElementInterface)) {
  417. if (!Element\Service::isPublished($data)) {
  418. return null;
  419. }
  420. }
  421. return $data;
  422. }
  423. /**
  424. * { @inheritdoc }
  425. */
  426. public function preSetData(/** mixed */ $container, /** mixed */ $data, /** array */ $params = []) // : mixed
  427. {
  428. $this->markLazyloadedFieldAsLoaded($container);
  429. return $data;
  430. }
  431. /**
  432. * @param string $assetUploadPath
  433. *
  434. * @return $this
  435. */
  436. public function setAssetUploadPath($assetUploadPath)
  437. {
  438. $this->assetUploadPath = $assetUploadPath;
  439. return $this;
  440. }
  441. /**
  442. * @return string
  443. */
  444. public function getAssetUploadPath()
  445. {
  446. return $this->assetUploadPath;
  447. }
  448. /**
  449. * {@inheritdoc}
  450. */
  451. public function isDiffChangeAllowed($object, $params = [])
  452. {
  453. return true;
  454. }
  455. /**
  456. * { @inheritdoc }
  457. */
  458. public function rewriteIds(/** mixed */ $container, /** array */ $idMapping, /** array */ $params = []) /** :mixed */
  459. {
  460. $data = $this->getDataFromObjectParam($container, $params);
  461. if ($data) {
  462. $data = $this->rewriteIdsService([$data], $idMapping);
  463. $data = $data[0]; //get the first element
  464. }
  465. return $data;
  466. }
  467. /**
  468. * @param DataObject\ClassDefinition\Data\ManyToOneRelation $masterDefinition
  469. */
  470. public function synchronizeWithMasterDefinition(DataObject\ClassDefinition\Data $masterDefinition)
  471. {
  472. $this->assetUploadPath = $masterDefinition->assetUploadPath;
  473. $this->relationType = $masterDefinition->relationType;
  474. }
  475. /**
  476. * {@inheritdoc}
  477. */
  478. protected function getPhpdocType()
  479. {
  480. return implode(' | ', $this->getPhpDocClassString(false));
  481. }
  482. /**
  483. * {@inheritdoc}
  484. */
  485. public function normalize($value, $params = [])
  486. {
  487. if ($value) {
  488. $type = Element\Service::getType($value);
  489. $id = $value->getId();
  490. return [
  491. 'type' => $type,
  492. 'id' => $id,
  493. ];
  494. }
  495. return null;
  496. }
  497. /**
  498. * {@inheritdoc}
  499. */
  500. public function denormalize($value, $params = [])
  501. {
  502. if (is_array($value)) {
  503. $type = $value['type'];
  504. $id = $value['id'];
  505. return Element\Service::getElementById($type, $id);
  506. }
  507. return null;
  508. }
  509. /**
  510. * @param Element\ElementInterface|null $value1
  511. * @param Element\ElementInterface|null $value2
  512. *
  513. * @return bool
  514. */
  515. public function isEqual($value1, $value2): bool
  516. {
  517. $value1 = $value1 ? $value1->getType() . $value1->getId() : null;
  518. $value2 = $value2 ? $value2->getType() . $value2->getId() : null;
  519. return $value1 === $value2;
  520. }
  521. /**
  522. * {@inheritdoc}
  523. */
  524. public function isFilterable(): bool
  525. {
  526. return true;
  527. }
  528. /**
  529. * {@inheritdoc}
  530. */
  531. public function getParameterTypeDeclaration(): ?string
  532. {
  533. return '?\\' . Element\AbstractElement::class;
  534. }
  535. /**
  536. * {@inheritdoc}
  537. */
  538. public function getReturnTypeDeclaration(): ?string
  539. {
  540. return '?\\' . Element\AbstractElement::class;
  541. }
  542. /**
  543. * {@inheritdoc}
  544. */
  545. public function addListingFilter(DataObject\Listing $listing, $data, $operator = '=')
  546. {
  547. if ($data instanceof Element\ElementInterface) {
  548. $data = [
  549. 'id' => $data->getId(),
  550. 'type' => Element\Service::getElementType($data),
  551. ];
  552. }
  553. if (!isset($data['id'], $data['type'])) {
  554. throw new \InvalidArgumentException('Please provide an array with keys "id" and "type" or an object which implements '.Element\ElementInterface::class);
  555. }
  556. if ($operator === '=') {
  557. $listing->addConditionParam('`'.$this->getName().'__id` = ? AND `'.$this->getName().'__type` = ?', [$data['id'], $data['type']]);
  558. return $listing;
  559. }
  560. throw new \InvalidArgumentException('Filtering '.__CLASS__.' does only support "=" operator');
  561. }
  562. /**
  563. * {@inheritdoc}
  564. */
  565. public function getPhpdocReturnType(): ?string
  566. {
  567. if ($phpdocType = $this->getPhpdocType()) {
  568. return $phpdocType . '|null';
  569. }
  570. return null;
  571. }
  572. }