Page.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Cms\Model\ResourceModel;
  7. use Magento\Cms\Api\Data\PageInterface;
  8. use Magento\Cms\Model\Page as CmsPage;
  9. use Magento\Framework\DB\Select;
  10. use Magento\Framework\EntityManager\EntityManager;
  11. use Magento\Framework\EntityManager\MetadataPool;
  12. use Magento\Framework\Exception\LocalizedException;
  13. use Magento\Framework\Model\AbstractModel;
  14. use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
  15. use Magento\Framework\Model\ResourceModel\Db\Context;
  16. use Magento\Framework\Stdlib\DateTime;
  17. use Magento\Store\Model\Store;
  18. use Magento\Store\Model\StoreManagerInterface;
  19. /**
  20. * Cms page mysql resource
  21. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  22. */
  23. class Page extends AbstractDb
  24. {
  25. /**
  26. * Store model
  27. *
  28. * @var null|Store
  29. */
  30. protected $_store = null;
  31. /**
  32. * Store manager
  33. *
  34. * @var StoreManagerInterface
  35. */
  36. protected $_storeManager;
  37. /**
  38. * @var DateTime
  39. */
  40. protected $dateTime;
  41. /**
  42. * @var EntityManager
  43. */
  44. protected $entityManager;
  45. /**
  46. * @var MetadataPool
  47. */
  48. protected $metadataPool;
  49. /**
  50. * @param Context $context
  51. * @param StoreManagerInterface $storeManager
  52. * @param DateTime $dateTime
  53. * @param EntityManager $entityManager
  54. * @param MetadataPool $metadataPool
  55. * @param string $connectionName
  56. */
  57. public function __construct(
  58. Context $context,
  59. StoreManagerInterface $storeManager,
  60. DateTime $dateTime,
  61. EntityManager $entityManager,
  62. MetadataPool $metadataPool,
  63. $connectionName = null
  64. ) {
  65. parent::__construct($context, $connectionName);
  66. $this->_storeManager = $storeManager;
  67. $this->dateTime = $dateTime;
  68. $this->entityManager = $entityManager;
  69. $this->metadataPool = $metadataPool;
  70. }
  71. /**
  72. * Initialize resource model
  73. *
  74. * @return void
  75. */
  76. protected function _construct()
  77. {
  78. $this->_init('cms_page', 'page_id');
  79. }
  80. /**
  81. * @inheritDoc
  82. */
  83. public function getConnection()
  84. {
  85. return $this->metadataPool->getMetadata(PageInterface::class)->getEntityConnection();
  86. }
  87. /**
  88. * Process page data before saving
  89. *
  90. * @param AbstractModel $object
  91. * @return $this
  92. * @throws LocalizedException
  93. */
  94. protected function _beforeSave(AbstractModel $object)
  95. {
  96. /*
  97. * For two attributes which represent timestamp data in DB
  98. * we should make converting such as:
  99. * If they are empty we need to convert them into DB
  100. * type NULL so in DB they will be empty and not some default value
  101. */
  102. foreach (['custom_theme_from', 'custom_theme_to'] as $field) {
  103. $value = !$object->getData($field) ? null : $this->dateTime->formatDate($object->getData($field));
  104. $object->setData($field, $value);
  105. }
  106. if (!$this->isValidPageIdentifier($object)) {
  107. throw new LocalizedException(
  108. __(
  109. "The page URL key can't use capital letters or disallowed symbols. "
  110. . "Remove the letters and symbols and try again."
  111. )
  112. );
  113. }
  114. if ($this->isNumericPageIdentifier($object)) {
  115. throw new LocalizedException(
  116. __("The page URL key can't use only numbers. Add letters or words and try again.")
  117. );
  118. }
  119. return parent::_beforeSave($object);
  120. }
  121. /**
  122. * @param AbstractModel $object
  123. * @param string $value
  124. * @param string|null $field
  125. * @return bool|int|string
  126. * @throws LocalizedException
  127. * @throws \Exception
  128. */
  129. private function getPageId(AbstractModel $object, $value, $field = null)
  130. {
  131. $entityMetadata = $this->metadataPool->getMetadata(PageInterface::class);
  132. if (!is_numeric($value) && $field === null) {
  133. $field = 'identifier';
  134. } elseif (!$field) {
  135. $field = $entityMetadata->getIdentifierField();
  136. }
  137. $pageId = $value;
  138. if ($field != $entityMetadata->getIdentifierField() || $object->getStoreId()) {
  139. $select = $this->_getLoadSelect($field, $value, $object);
  140. $select->reset(Select::COLUMNS)
  141. ->columns($this->getMainTable() . '.' . $entityMetadata->getIdentifierField())
  142. ->limit(1);
  143. $result = $this->getConnection()->fetchCol($select);
  144. $pageId = count($result) ? $result[0] : false;
  145. }
  146. return $pageId;
  147. }
  148. /**
  149. * Load an object
  150. *
  151. * @param CmsPage|AbstractModel $object
  152. * @param mixed $value
  153. * @param string $field field to load by (defaults to model id)
  154. * @return $this
  155. */
  156. public function load(AbstractModel $object, $value, $field = null)
  157. {
  158. $pageId = $this->getPageId($object, $value, $field);
  159. if ($pageId) {
  160. $this->entityManager->load($object, $pageId);
  161. }
  162. return $this;
  163. }
  164. /**
  165. * Retrieve select object for load object data
  166. *
  167. * @param string $field
  168. * @param mixed $value
  169. * @param CmsPage|AbstractModel $object
  170. * @return Select
  171. */
  172. protected function _getLoadSelect($field, $value, $object)
  173. {
  174. $entityMetadata = $this->metadataPool->getMetadata(PageInterface::class);
  175. $linkField = $entityMetadata->getLinkField();
  176. $select = parent::_getLoadSelect($field, $value, $object);
  177. if ($object->getStoreId()) {
  178. $storeIds = [
  179. Store::DEFAULT_STORE_ID,
  180. (int)$object->getStoreId(),
  181. ];
  182. $select->join(
  183. ['cms_page_store' => $this->getTable('cms_page_store')],
  184. $this->getMainTable() . '.' . $linkField . ' = cms_page_store.' . $linkField,
  185. []
  186. )
  187. ->where('is_active = ?', 1)
  188. ->where('cms_page_store.store_id IN (?)', $storeIds)
  189. ->order('cms_page_store.store_id DESC')
  190. ->limit(1);
  191. }
  192. return $select;
  193. }
  194. /**
  195. * Retrieve load select with filter by identifier, store and activity
  196. *
  197. * @param string $identifier
  198. * @param int|array $store
  199. * @param int $isActive
  200. * @return Select
  201. */
  202. protected function _getLoadByIdentifierSelect($identifier, $store, $isActive = null)
  203. {
  204. $entityMetadata = $this->metadataPool->getMetadata(PageInterface::class);
  205. $linkField = $entityMetadata->getLinkField();
  206. $select = $this->getConnection()->select()
  207. ->from(['cp' => $this->getMainTable()])
  208. ->join(
  209. ['cps' => $this->getTable('cms_page_store')],
  210. 'cp.' . $linkField . ' = cps.' . $linkField,
  211. []
  212. )
  213. ->where('cp.identifier = ?', $identifier)
  214. ->where('cps.store_id IN (?)', $store);
  215. if ($isActive !== null) {
  216. $select->where('cp.is_active = ?', $isActive);
  217. }
  218. return $select;
  219. }
  220. /**
  221. * Check whether page identifier is numeric
  222. *
  223. * @param AbstractModel $object
  224. * @return bool
  225. */
  226. protected function isNumericPageIdentifier(AbstractModel $object)
  227. {
  228. return preg_match('/^[0-9]+$/', $object->getData('identifier'));
  229. }
  230. /**
  231. * Check whether page identifier is valid
  232. *
  233. * @param AbstractModel $object
  234. * @return bool
  235. */
  236. protected function isValidPageIdentifier(AbstractModel $object)
  237. {
  238. return preg_match('/^[a-z0-9][a-z0-9_\/-]+(\.[a-z0-9_-]+)?$/', $object->getData('identifier'));
  239. }
  240. /**
  241. * Check if page identifier exist for specific store
  242. * return page id if page exists
  243. *
  244. * @param string $identifier
  245. * @param int $storeId
  246. * @return int
  247. */
  248. public function checkIdentifier($identifier, $storeId)
  249. {
  250. $entityMetadata = $this->metadataPool->getMetadata(PageInterface::class);
  251. $stores = [Store::DEFAULT_STORE_ID, $storeId];
  252. $select = $this->_getLoadByIdentifierSelect($identifier, $stores, 1);
  253. $select->reset(Select::COLUMNS)
  254. ->columns('cp.' . $entityMetadata->getIdentifierField())
  255. ->order('cps.store_id DESC')
  256. ->limit(1);
  257. return $this->getConnection()->fetchOne($select);
  258. }
  259. /**
  260. * Retrieves cms page title from DB by passed identifier.
  261. *
  262. * @param string $identifier
  263. * @return string|false
  264. */
  265. public function getCmsPageTitleByIdentifier($identifier)
  266. {
  267. $stores = [Store::DEFAULT_STORE_ID];
  268. if ($this->_store) {
  269. $stores[] = (int)$this->getStore()->getId();
  270. }
  271. $select = $this->_getLoadByIdentifierSelect($identifier, $stores);
  272. $select->reset(Select::COLUMNS)
  273. ->columns('cp.title')
  274. ->order('cps.store_id DESC')
  275. ->limit(1);
  276. return $this->getConnection()->fetchOne($select);
  277. }
  278. /**
  279. * Retrieves cms page title from DB by passed id.
  280. *
  281. * @param string $id
  282. * @return string|false
  283. */
  284. public function getCmsPageTitleById($id)
  285. {
  286. $connection = $this->getConnection();
  287. $entityMetadata = $this->metadataPool->getMetadata(PageInterface::class);
  288. $select = $connection->select()
  289. ->from($this->getMainTable(), 'title')
  290. ->where($entityMetadata->getIdentifierField() . ' = :page_id');
  291. return $connection->fetchOne($select, ['page_id' => (int)$id]);
  292. }
  293. /**
  294. * Retrieves cms page identifier from DB by passed id.
  295. *
  296. * @param string $id
  297. * @return string|false
  298. */
  299. public function getCmsPageIdentifierById($id)
  300. {
  301. $connection = $this->getConnection();
  302. $entityMetadata = $this->metadataPool->getMetadata(PageInterface::class);
  303. $select = $connection->select()
  304. ->from($this->getMainTable(), 'identifier')
  305. ->where($entityMetadata->getIdentifierField() . ' = :page_id');
  306. return $connection->fetchOne($select, ['page_id' => (int)$id]);
  307. }
  308. /**
  309. * Get store ids to which specified item is assigned
  310. *
  311. * @param int $pageId
  312. * @return array
  313. */
  314. public function lookupStoreIds($pageId)
  315. {
  316. $connection = $this->getConnection();
  317. $entityMetadata = $this->metadataPool->getMetadata(PageInterface::class);
  318. $linkField = $entityMetadata->getLinkField();
  319. $select = $connection->select()
  320. ->from(['cps' => $this->getTable('cms_page_store')], 'store_id')
  321. ->join(
  322. ['cp' => $this->getMainTable()],
  323. 'cps.' . $linkField . ' = cp.' . $linkField,
  324. []
  325. )
  326. ->where('cp.' . $entityMetadata->getIdentifierField() . ' = :page_id');
  327. return $connection->fetchCol($select, ['page_id' => (int)$pageId]);
  328. }
  329. /**
  330. * Set store model
  331. *
  332. * @param Store $store
  333. * @return $this
  334. */
  335. public function setStore($store)
  336. {
  337. $this->_store = $store;
  338. return $this;
  339. }
  340. /**
  341. * Retrieve store model
  342. *
  343. * @return Store
  344. */
  345. public function getStore()
  346. {
  347. return $this->_storeManager->getStore($this->_store);
  348. }
  349. /**
  350. * @inheritDoc
  351. */
  352. public function save(AbstractModel $object)
  353. {
  354. $this->entityManager->save($object);
  355. return $this;
  356. }
  357. /**
  358. * @inheritDoc
  359. */
  360. public function delete(AbstractModel $object)
  361. {
  362. $this->entityManager->delete($object);
  363. return $this;
  364. }
  365. }