Store.php 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Store\Model;
  7. use Magento\Catalog\Model\Category;
  8. use Magento\Directory\Model\Currency;
  9. use Magento\Framework\App\Config\ScopeConfigInterface;
  10. use Magento\Framework\App\Filesystem\DirectoryList;
  11. use Magento\Framework\App\Http\Context;
  12. use Magento\Framework\App\ObjectManager;
  13. use Magento\Framework\App\ScopeInterface as AppScopeInterface;
  14. use Magento\Framework\DataObject\IdentityInterface;
  15. use Magento\Framework\Filesystem;
  16. use Magento\Framework\Model\AbstractExtensibleModel;
  17. use Magento\Framework\Url\ScopeInterface as UrlScopeInterface;
  18. use Magento\Framework\UrlInterface;
  19. use Magento\Store\Api\Data\StoreInterface;
  20. use Zend\Uri\UriFactory;
  21. /**
  22. * Store model
  23. *
  24. * @api
  25. * @method Store setGroupId($value)
  26. * @method int getSortOrder()
  27. * @method int getStoreId()
  28. * @method Store setSortOrder($value)
  29. *
  30. * @SuppressWarnings(PHPMD.TooManyFields)
  31. * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
  32. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  33. * @SuppressWarnings(PHPMD.ExcessivePublicCount)
  34. * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
  35. * @since 100.0.2
  36. */
  37. class Store extends AbstractExtensibleModel implements
  38. AppScopeInterface,
  39. UrlScopeInterface,
  40. IdentityInterface,
  41. StoreInterface
  42. {
  43. /**
  44. * Store Id key name
  45. */
  46. const STORE_ID = 'store_id';
  47. /**
  48. * Entity name
  49. */
  50. const ENTITY = 'store';
  51. /**
  52. * Custom entry point param
  53. */
  54. const CUSTOM_ENTRY_POINT_PARAM = 'custom_entry_point';
  55. /**#@+
  56. * Configuration paths
  57. */
  58. const XML_PATH_STORE_IN_URL = 'web/url/use_store';
  59. const XML_PATH_USE_REWRITES = 'web/seo/use_rewrites';
  60. const XML_PATH_UNSECURE_BASE_URL = 'web/unsecure/base_url';
  61. const XML_PATH_SECURE_BASE_URL = 'web/secure/base_url';
  62. const XML_PATH_SECURE_IN_FRONTEND = 'web/secure/use_in_frontend';
  63. const XML_PATH_SECURE_IN_ADMINHTML = 'web/secure/use_in_adminhtml';
  64. const XML_PATH_ENABLE_HSTS = 'web/secure/enable_hsts';
  65. const XML_PATH_ENABLE_UPGRADE_INSECURE = 'web/secure/enable_upgrade_insecure';
  66. const XML_PATH_SECURE_BASE_LINK_URL = 'web/secure/base_link_url';
  67. const XML_PATH_UNSECURE_BASE_LINK_URL = 'web/unsecure/base_link_url';
  68. const XML_PATH_SECURE_BASE_STATIC_URL = 'web/secure/base_static_url';
  69. const XML_PATH_UNSECURE_BASE_STATIC_URL = 'web/unsecure/base_static_url';
  70. const XML_PATH_SECURE_BASE_MEDIA_URL = 'web/secure/base_media_url';
  71. const XML_PATH_UNSECURE_BASE_MEDIA_URL = 'web/unsecure/base_media_url';
  72. const XML_PATH_PRICE_SCOPE = 'catalog/price/scope';
  73. /**#@-*/
  74. /**#@+
  75. * Price scope constants
  76. */
  77. const PRICE_SCOPE_GLOBAL = 0;
  78. const PRICE_SCOPE_WEBSITE = 1;
  79. /**#@-*/
  80. const ADMIN_CODE = 'admin';
  81. /**
  82. * Cache tag
  83. */
  84. const CACHE_TAG = 'store';
  85. /**
  86. * Script name, which returns all the images
  87. */
  88. const MEDIA_REWRITE_SCRIPT = 'get.php/';
  89. /**
  90. * A placeholder for generating base URL
  91. */
  92. const BASE_URL_PLACEHOLDER = '{{base_url}}';
  93. /**
  94. * Identifier of default store
  95. * used for loading data of default scope
  96. */
  97. const DEFAULT_STORE_ID = 0;
  98. /**
  99. * Default store Id (for install)
  100. */
  101. const DISTRO_STORE_ID = 1;
  102. /**
  103. * @var \Magento\Framework\App\Cache\Type\Config
  104. */
  105. protected $_configCacheType;
  106. /**
  107. * Cache flag
  108. *
  109. * @var boolean
  110. */
  111. protected $_cacheTag = true;
  112. /**
  113. * Event prefix for model events
  114. *
  115. * @var string
  116. */
  117. protected $_eventPrefix = 'store';
  118. /**
  119. * Event object name
  120. *
  121. * @var string
  122. */
  123. protected $_eventObject = 'store';
  124. /**
  125. * Price filter
  126. *
  127. * @var \Magento\Directory\Model\Currency\Filter
  128. */
  129. protected $_priceFilter;
  130. /**
  131. * Store configuration cache
  132. *
  133. * @var array|null
  134. */
  135. protected $_configCache = null;
  136. /**
  137. * Base nodes of store configuration cache
  138. *
  139. * @var array
  140. */
  141. protected $_configCacheBaseNodes = [];
  142. /**
  143. * Directory cache
  144. *
  145. * @var array
  146. */
  147. protected $_dirCache = [];
  148. /**
  149. * URL cache
  150. *
  151. * @var array
  152. */
  153. protected $_urlCache = [];
  154. /**
  155. * Base URL cache
  156. *
  157. * @var array
  158. */
  159. protected $_baseUrlCache = [];
  160. /**
  161. * Session entity
  162. *
  163. * @var \Magento\Framework\Session\SessionManagerInterface
  164. */
  165. protected $_session;
  166. /**
  167. * Flag that shows that backend URLs are secure
  168. *
  169. * @var boolean|null
  170. * @deprecated 101.0.0 unused protected property
  171. */
  172. protected $_isAdminSecure = null;
  173. /**
  174. * Flag that shows that frontend URLs are secure
  175. *
  176. * @var boolean|null
  177. */
  178. protected $_isFrontSecure = null;
  179. /**
  180. * Store frontend name
  181. *
  182. * @var string|null
  183. */
  184. protected $_frontendName = null;
  185. /**
  186. * Readonly flag
  187. *
  188. * @var bool
  189. */
  190. private $_isReadOnly = false;
  191. /**
  192. * Url model for current store
  193. *
  194. * @var \Magento\Framework\UrlInterface
  195. */
  196. protected $_url;
  197. /**
  198. * @var bool
  199. */
  200. protected $_isCustomEntryPoint = false;
  201. /**
  202. * @var \Magento\Framework\App\RequestInterface
  203. */
  204. protected $_request;
  205. /**
  206. * @var \Magento\Config\Model\ResourceModel\Config\Data
  207. */
  208. protected $_configDataResource;
  209. /**
  210. * Core file storage database
  211. *
  212. * @var \Magento\MediaStorage\Helper\File\Storage\Database
  213. */
  214. protected $_coreFileStorageDatabase = null;
  215. /**
  216. * Filesystem instance
  217. *
  218. * @var Filesystem
  219. */
  220. protected $filesystem;
  221. /**
  222. * Store Config
  223. *
  224. * @var \Magento\Framework\App\Config\ReinitableConfigInterface
  225. */
  226. protected $_config;
  227. /**
  228. * @var \Magento\Framework\Session\SidResolverInterface
  229. */
  230. protected $_sidResolver;
  231. /**
  232. * @var string
  233. */
  234. protected $_currencyInstalled;
  235. /**
  236. * @var \Magento\Framework\App\Http\Context
  237. */
  238. protected $_httpContext;
  239. /**
  240. * @var \Magento\Directory\Model\CurrencyFactory
  241. */
  242. protected $currencyFactory;
  243. /**
  244. * @var \Magento\Store\Api\GroupRepositoryInterface
  245. */
  246. protected $groupRepository;
  247. /**
  248. * @var \Magento\Store\Api\WebsiteRepositoryInterface
  249. */
  250. protected $websiteRepository;
  251. /**
  252. * @var \Magento\Store\Model\Information
  253. */
  254. protected $information;
  255. /**
  256. * @var StoreManagerInterface
  257. */
  258. private $_storeManager;
  259. /**
  260. * @var \Magento\Framework\Url\ModifierInterface
  261. */
  262. private $urlModifier;
  263. /**
  264. * @var \Magento\Framework\Event\ManagerInterface
  265. */
  266. private $eventManager;
  267. /**
  268. * @param \Magento\Framework\Model\Context $context
  269. * @param \Magento\Framework\Registry $registry
  270. * @param \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory
  271. * @param \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory
  272. * @param \Magento\Store\Model\ResourceModel\Store $resource
  273. * @param \Magento\MediaStorage\Helper\File\Storage\Database $coreFileStorageDatabase
  274. * @param \Magento\Framework\App\Cache\Type\Config $configCacheType
  275. * @param \Magento\Framework\UrlInterface $url
  276. * @param \Magento\Framework\App\RequestInterface $request
  277. * @param \Magento\Config\Model\ResourceModel\Config\Data $configDataResource
  278. * @param \Magento\Framework\Filesystem $filesystem
  279. * @param \Magento\Framework\App\Config\ReinitableConfigInterface $config
  280. * @param \Magento\Store\Model\StoreManagerInterface $storeManager
  281. * @param \Magento\Framework\Session\SidResolverInterface $sidResolver
  282. * @param \Magento\Framework\App\Http\Context $httpContext
  283. * @param \Magento\Framework\Session\SessionManagerInterface $session
  284. * @param \Magento\Directory\Model\CurrencyFactory $currencyFactory
  285. * @param Information $information
  286. * @param string $currencyInstalled
  287. * @param \Magento\Store\Api\GroupRepositoryInterface $groupRepository
  288. * @param \Magento\Store\Api\WebsiteRepositoryInterface $websiteRepository
  289. * @param \Magento\Framework\Data\Collection\AbstractDb $resourceCollection
  290. * @param bool $isCustomEntryPoint
  291. * @param array $data optional generic object data
  292. * @param \Magento\Framework\Event\ManagerInterface|null $eventManager
  293. *
  294. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  295. */
  296. public function __construct(
  297. \Magento\Framework\Model\Context $context,
  298. \Magento\Framework\Registry $registry,
  299. \Magento\Framework\Api\ExtensionAttributesFactory $extensionFactory,
  300. \Magento\Framework\Api\AttributeValueFactory $customAttributeFactory,
  301. \Magento\Store\Model\ResourceModel\Store $resource,
  302. \Magento\MediaStorage\Helper\File\Storage\Database $coreFileStorageDatabase,
  303. \Magento\Framework\App\Cache\Type\Config $configCacheType,
  304. \Magento\Framework\UrlInterface $url,
  305. \Magento\Framework\App\RequestInterface $request,
  306. \Magento\Config\Model\ResourceModel\Config\Data $configDataResource,
  307. \Magento\Framework\Filesystem $filesystem,
  308. \Magento\Framework\App\Config\ReinitableConfigInterface $config,
  309. \Magento\Store\Model\StoreManagerInterface $storeManager,
  310. \Magento\Framework\Session\SidResolverInterface $sidResolver,
  311. \Magento\Framework\App\Http\Context $httpContext,
  312. \Magento\Framework\Session\SessionManagerInterface $session,
  313. \Magento\Directory\Model\CurrencyFactory $currencyFactory,
  314. \Magento\Store\Model\Information $information,
  315. $currencyInstalled,
  316. \Magento\Store\Api\GroupRepositoryInterface $groupRepository,
  317. \Magento\Store\Api\WebsiteRepositoryInterface $websiteRepository,
  318. \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
  319. $isCustomEntryPoint = false,
  320. array $data = [],
  321. \Magento\Framework\Event\ManagerInterface $eventManager = null
  322. ) {
  323. $this->_coreFileStorageDatabase = $coreFileStorageDatabase;
  324. $this->_config = $config;
  325. $this->_url = $url;
  326. $this->_configCacheType = $configCacheType;
  327. $this->_request = $request;
  328. $this->_configDataResource = $configDataResource;
  329. $this->_isCustomEntryPoint = $isCustomEntryPoint;
  330. $this->filesystem = $filesystem;
  331. $this->_storeManager = $storeManager;
  332. $this->_sidResolver = $sidResolver;
  333. $this->_httpContext = $httpContext;
  334. $this->_session = $session;
  335. $this->currencyFactory = $currencyFactory;
  336. $this->information = $information;
  337. $this->_currencyInstalled = $currencyInstalled;
  338. $this->groupRepository = $groupRepository;
  339. $this->websiteRepository = $websiteRepository;
  340. $this->eventManager = $eventManager ?: \Magento\Framework\App\ObjectManager::getInstance()
  341. ->get(\Magento\Framework\Event\ManagerInterface::class);
  342. parent::__construct(
  343. $context,
  344. $registry,
  345. $extensionFactory,
  346. $customAttributeFactory,
  347. $resource,
  348. $resourceCollection,
  349. $data
  350. );
  351. }
  352. /**
  353. * @inheritdoc
  354. */
  355. public function __sleep()
  356. {
  357. $properties = parent::__sleep();
  358. $properties = array_diff($properties, ['_coreFileStorageDatabase', '_config']);
  359. return $properties;
  360. }
  361. /**
  362. * Init not serializable fields
  363. *
  364. * @return void
  365. */
  366. public function __wakeup()
  367. {
  368. parent::__wakeup();
  369. $this->_coreFileStorageDatabase = ObjectManager::getInstance()
  370. ->get(\Magento\MediaStorage\Helper\File\Storage\Database::class);
  371. $this->_config = ObjectManager::getInstance()->get(
  372. \Magento\Framework\App\Config\ReinitableConfigInterface::class
  373. );
  374. }
  375. /**
  376. * Initialize object
  377. *
  378. * @return void
  379. */
  380. protected function _construct()
  381. {
  382. $this->_init(\Magento\Store\Model\ResourceModel\Store::class);
  383. }
  384. /**
  385. * Retrieve store session object
  386. *
  387. * @return \Magento\Framework\Session\SessionManagerInterface
  388. */
  389. protected function _getSession()
  390. {
  391. if (!$this->_session->isSessionExists()) {
  392. $this->_session->setName('store_' . $this->getCode());
  393. $this->_session->start();
  394. }
  395. return $this->_session;
  396. }
  397. /**
  398. * Validation rules for store
  399. *
  400. * @return \Zend_Validate_Interface|null
  401. * @throws \Zend_Validate_Exception
  402. */
  403. protected function _getValidationRulesBeforeSave()
  404. {
  405. $validator = new \Magento\Framework\Validator\DataObject();
  406. $storeLabelRule = new \Zend_Validate_NotEmpty();
  407. $storeLabelRule->setMessage(__('Name is required'), \Zend_Validate_NotEmpty::IS_EMPTY);
  408. $validator->addRule($storeLabelRule, 'name');
  409. $storeCodeRule = new \Zend_Validate_Regex('/^[a-z]+[a-z0-9_]*$/i');
  410. $storeCodeRule->setMessage(
  411. __(
  412. 'The store code may contain only letters (a-z), numbers (0-9) or underscore (_),'
  413. . ' and the first character must be a letter.'
  414. ),
  415. \Zend_Validate_Regex::NOT_MATCH
  416. );
  417. $validator->addRule($storeCodeRule, 'code');
  418. return $validator;
  419. }
  420. /**
  421. * Loading store data
  422. *
  423. * @param mixed $key
  424. * @param string $field
  425. *
  426. * @return $this
  427. * @throws \Magento\Framework\Exception\LocalizedException
  428. */
  429. public function load($key, $field = null)
  430. {
  431. if (!is_numeric($key) && $field === null) {
  432. $this->_getResource()->load($this, $key, 'code');
  433. return $this;
  434. }
  435. return parent::load($key, $field);
  436. }
  437. /**
  438. * Retrieve Store code
  439. *
  440. * @return string
  441. */
  442. public function getCode()
  443. {
  444. return $this->_getData('code');
  445. }
  446. /**
  447. * @inheritdoc
  448. */
  449. public function setCode($code)
  450. {
  451. return $this->setData('code', $code);
  452. }
  453. /**
  454. * @inheritdoc
  455. */
  456. public function getName()
  457. {
  458. return $this->_getData('name');
  459. }
  460. /**
  461. * @inheritdoc
  462. */
  463. public function setName($name)
  464. {
  465. return $this->setData('name', $name);
  466. }
  467. /**
  468. * Retrieve store configuration data
  469. *
  470. * @param string $path
  471. * @return string|null
  472. */
  473. public function getConfig($path)
  474. {
  475. $data = $this->_config->getValue($path, ScopeInterface::SCOPE_STORE, $this->getCode());
  476. if ($data === null) {
  477. $data = $this->_config->getValue($path);
  478. }
  479. return $data === false ? null : $data;
  480. }
  481. /**
  482. * Set relation to the website
  483. *
  484. * @param Website $website
  485. * @return void
  486. */
  487. public function setWebsite(Website $website)
  488. {
  489. $this->setWebsiteId($website->getId());
  490. }
  491. /**
  492. * Retrieve store website
  493. *
  494. * @return Website|bool
  495. * @throws \Magento\Framework\Exception\NoSuchEntityException
  496. */
  497. public function getWebsite()
  498. {
  499. if ($this->getWebsiteId() === null) {
  500. return false;
  501. }
  502. return $this->websiteRepository->getById($this->getWebsiteId());
  503. }
  504. /**
  505. * Retrieve url using store configuration specific
  506. *
  507. * @param string $route
  508. * @param array $params
  509. *
  510. * @return string
  511. * @throws \Magento\Framework\Exception\NoSuchEntityException
  512. */
  513. public function getUrl($route = '', $params = [])
  514. {
  515. /** @var $url UrlInterface */
  516. $url = $this->_url->setScope($this);
  517. if ($this->_storeManager->getStore()->getId() != $this->getId()) {
  518. $params['_scope_to_url'] = true;
  519. }
  520. return $url->getUrl($route, $params);
  521. }
  522. /**
  523. * Retrieve base URL
  524. *
  525. * @param string $type
  526. * @param boolean|null $secure
  527. * @return string
  528. * @throws \InvalidArgumentException
  529. *
  530. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  531. * @SuppressWarnings(PHPMD.NPathComplexity)
  532. */
  533. public function getBaseUrl($type = UrlInterface::URL_TYPE_LINK, $secure = null)
  534. {
  535. $cacheKey = $type . '/' . ($secure === null ? 'null' : ($secure ? 'true' : 'false'));
  536. if (!isset($this->_baseUrlCache[$cacheKey])) {
  537. $secure = $secure === null ? $this->isCurrentlySecure() : (bool)$secure;
  538. switch ($type) {
  539. case UrlInterface::URL_TYPE_WEB:
  540. $path = $secure
  541. ? self::XML_PATH_SECURE_BASE_URL
  542. : self::XML_PATH_UNSECURE_BASE_URL;
  543. $url = $this->getConfig($path);
  544. break;
  545. case UrlInterface::URL_TYPE_LINK:
  546. $path = $secure ? self::XML_PATH_SECURE_BASE_LINK_URL : self::XML_PATH_UNSECURE_BASE_LINK_URL;
  547. $url = $this->getConfig($path);
  548. $url = $this->_updatePathUseRewrites($url);
  549. $url = $this->_updatePathUseStoreView($url);
  550. break;
  551. case UrlInterface::URL_TYPE_DIRECT_LINK:
  552. $path = $secure ? self::XML_PATH_SECURE_BASE_LINK_URL : self::XML_PATH_UNSECURE_BASE_LINK_URL;
  553. $url = $this->getConfig($path);
  554. $url = $this->_updatePathUseRewrites($url);
  555. break;
  556. case UrlInterface::URL_TYPE_STATIC:
  557. $path = $secure ? self::XML_PATH_SECURE_BASE_STATIC_URL : self::XML_PATH_UNSECURE_BASE_STATIC_URL;
  558. $url = $this->getConfig($path);
  559. if (!$url) {
  560. $url = $this->getBaseUrl(UrlInterface::URL_TYPE_WEB, $secure)
  561. . $this->filesystem->getUri(DirectoryList::STATIC_VIEW);
  562. }
  563. break;
  564. case UrlInterface::URL_TYPE_MEDIA:
  565. $url = $this->_getMediaScriptUrl($this->filesystem, $secure);
  566. if (!$url) {
  567. $path = $secure ? self::XML_PATH_SECURE_BASE_MEDIA_URL : self::XML_PATH_UNSECURE_BASE_MEDIA_URL;
  568. $url = $this->getConfig($path);
  569. if (!$url) {
  570. $url = $this->getBaseUrl(UrlInterface::URL_TYPE_WEB, $secure)
  571. . $this->filesystem->getUri(DirectoryList::MEDIA);
  572. }
  573. }
  574. break;
  575. default:
  576. throw new \InvalidArgumentException('Invalid base url type');
  577. }
  578. if (false !== strpos($url, self::BASE_URL_PLACEHOLDER)) {
  579. $url = str_replace(self::BASE_URL_PLACEHOLDER, $this->_request->getDistroBaseUrl(), $url);
  580. }
  581. $this->_baseUrlCache[$cacheKey] = $this->getUrlModifier()->execute(
  582. rtrim($url, '/') . '/',
  583. \Magento\Framework\Url\ModifierInterface::MODE_BASE
  584. );
  585. }
  586. return $this->_baseUrlCache[$cacheKey];
  587. }
  588. /**
  589. * Retrieve base media directory path
  590. *
  591. * @return string
  592. */
  593. public function getBaseMediaDir()
  594. {
  595. return $this->filesystem->getUri(DirectoryList::MEDIA);
  596. }
  597. /**
  598. * Retrieve base static directory path
  599. *
  600. * @return string
  601. */
  602. public function getBaseStaticDir()
  603. {
  604. return $this->filesystem->getUri(DirectoryList::STATIC_VIEW);
  605. }
  606. /**
  607. * Append script file name to url in case when server rewrites are disabled
  608. *
  609. * @param string $url
  610. * @return string
  611. */
  612. protected function _updatePathUseRewrites($url)
  613. {
  614. if ($this->getForceDisableRewrites() || !$this->getConfig(self::XML_PATH_USE_REWRITES)) {
  615. if ($this->_isCustomEntryPoint()) {
  616. $indexFileName = 'index.php';
  617. } else {
  618. $indexFileName = basename($_SERVER['SCRIPT_FILENAME']);
  619. }
  620. $url .= $indexFileName . '/';
  621. }
  622. return $url;
  623. }
  624. /**
  625. * Check if used entry point is custom
  626. *
  627. * @return bool
  628. */
  629. protected function _isCustomEntryPoint()
  630. {
  631. return $this->_isCustomEntryPoint;
  632. }
  633. /**
  634. * Retrieve URL for media catalog
  635. *
  636. * If we use Database file storage and server doesn't support rewrites (.htaccess in media folder)
  637. * we have to put name of fetching media script exactly into URL
  638. *
  639. * @param Filesystem $filesystem
  640. * @param bool $secure
  641. * @return string|bool
  642. */
  643. protected function _getMediaScriptUrl(Filesystem $filesystem, $secure)
  644. {
  645. if (!$this->getConfig(self::XML_PATH_USE_REWRITES) && $this->_coreFileStorageDatabase->checkDbUsage()) {
  646. $baseUrl = $this->getBaseUrl(UrlInterface::URL_TYPE_WEB, $secure);
  647. return $baseUrl . $filesystem->getUri(DirectoryList::PUB) . '/' . self::MEDIA_REWRITE_SCRIPT;
  648. }
  649. return false;
  650. }
  651. /**
  652. * Add store code to url in case if it is enabled in configuration
  653. *
  654. * @param string $url
  655. * @return string
  656. */
  657. protected function _updatePathUseStoreView($url)
  658. {
  659. if ($this->isUseStoreInUrl()) {
  660. $url .= $this->getCode() . '/';
  661. }
  662. return $url;
  663. }
  664. /**
  665. * Returns whether url forming scheme prepends url path with store view code
  666. *
  667. * @return boolean
  668. */
  669. public function isUseStoreInUrl()
  670. {
  671. return !($this->hasDisableStoreInUrl() && $this->getDisableStoreInUrl())
  672. && $this->getConfig(self::XML_PATH_STORE_IN_URL);
  673. }
  674. /**
  675. * Get store identifier
  676. *
  677. * @return int
  678. */
  679. public function getId()
  680. {
  681. return $this->_getData(self::STORE_ID);
  682. }
  683. /**
  684. * Check if frontend URLs should be secure
  685. *
  686. * @return boolean
  687. */
  688. public function isFrontUrlSecure()
  689. {
  690. if ($this->_isFrontSecure === null) {
  691. $this->_isFrontSecure = $this->_config
  692. ->isSetFlag(self::XML_PATH_SECURE_IN_FRONTEND, ScopeInterface::SCOPE_STORE, $this->getId());
  693. }
  694. return $this->_isFrontSecure;
  695. }
  696. /**
  697. * @inheritdoc
  698. */
  699. public function isUrlSecure()
  700. {
  701. return $this->isFrontUrlSecure();
  702. }
  703. /**
  704. * Check if request was secure
  705. *
  706. * @return boolean
  707. */
  708. public function isCurrentlySecure()
  709. {
  710. if ($this->_request->isSecure()) {
  711. return true;
  712. }
  713. $secureBaseUrl = $this->_config->getValue(self::XML_PATH_SECURE_BASE_URL, ScopeInterface::SCOPE_STORE);
  714. $secureFrontend = $this->_config->getValue(self::XML_PATH_SECURE_IN_FRONTEND, ScopeInterface::SCOPE_STORE);
  715. if (!$secureBaseUrl || !$secureFrontend) {
  716. return false;
  717. }
  718. $uri = UriFactory::factory($secureBaseUrl);
  719. $port = $uri->getPort();
  720. $serverPort = $this->_request->getServer('SERVER_PORT');
  721. $isSecure = $uri->getScheme() == 'https' && isset($serverPort) && $port == $serverPort;
  722. return $isSecure;
  723. }
  724. /*************************************************************************************
  725. * Store currency interface
  726. */
  727. /**
  728. * Retrieve store base currency code
  729. *
  730. * @return string
  731. */
  732. public function getBaseCurrencyCode()
  733. {
  734. $configValue = $this->getConfig(self::XML_PATH_PRICE_SCOPE);
  735. if ($configValue == self::PRICE_SCOPE_GLOBAL) {
  736. return $this->_config->getValue(Currency::XML_PATH_CURRENCY_BASE, ScopeConfigInterface::SCOPE_TYPE_DEFAULT);
  737. }
  738. return $this->getConfig(Currency::XML_PATH_CURRENCY_BASE);
  739. }
  740. /**
  741. * Retrieve store base currency
  742. *
  743. * @return Currency
  744. */
  745. public function getBaseCurrency()
  746. {
  747. $currency = $this->getData('base_currency');
  748. if (null === $currency) {
  749. $currency = $this->currencyFactory->create()->load($this->getBaseCurrencyCode());
  750. $this->setData('base_currency', $currency);
  751. }
  752. return $currency;
  753. }
  754. /**
  755. * Get default store currency code
  756. *
  757. * @return string
  758. */
  759. public function getDefaultCurrencyCode()
  760. {
  761. $result = $this->getConfig(Currency::XML_PATH_CURRENCY_DEFAULT);
  762. return $result;
  763. }
  764. /**
  765. * Retrieve store default currency
  766. *
  767. * @return Currency
  768. */
  769. public function getDefaultCurrency()
  770. {
  771. $currency = $this->getData('default_currency');
  772. if (null === $currency) {
  773. $currency = $this->currencyFactory->create()->load($this->getDefaultCurrencyCode());
  774. $this->setData('default_currency', $currency);
  775. }
  776. return $currency;
  777. }
  778. /**
  779. * Set current store currency code
  780. *
  781. * @param string $code
  782. *
  783. * @return string
  784. * @throws \Magento\Framework\Exception\LocalizedException
  785. */
  786. public function setCurrentCurrencyCode($code)
  787. {
  788. $code = strtoupper($code);
  789. if (in_array($code, $this->getAvailableCurrencyCodes())) {
  790. $this->_getSession()->setCurrencyCode($code);
  791. $defaultCode = ($this->_storeManager->getStore() !== null)
  792. ? $this->_storeManager->getStore()->getDefaultCurrency()->getCode()
  793. : $this->_storeManager->getWebsite()->getDefaultStore()->getDefaultCurrency()->getCode();
  794. $this->_httpContext->setValue(Context::CONTEXT_CURRENCY, $code, $defaultCode);
  795. }
  796. return $this;
  797. }
  798. /**
  799. * Get current store currency code
  800. *
  801. * @return string
  802. */
  803. public function getCurrentCurrencyCode()
  804. {
  805. $availableCurrencyCodes = \array_values($this->getAvailableCurrencyCodes(true));
  806. // try to get currently set code among allowed
  807. $code = $this->_httpContext->getValue(Context::CONTEXT_CURRENCY) ?? $this->_getSession()->getCurrencyCode();
  808. if (empty($code) || !\in_array($code, $availableCurrencyCodes)) {
  809. $code = $this->getDefaultCurrencyCode();
  810. if (!\in_array($code, $availableCurrencyCodes) && !empty($availableCurrencyCodes)) {
  811. $code = $availableCurrencyCodes[0];
  812. }
  813. }
  814. return $code;
  815. }
  816. /**
  817. * Get allowed store currency codes
  818. *
  819. * If base currency is not allowed in current website config scope,
  820. * then it can be disabled with $skipBaseNotAllowed
  821. *
  822. * @param bool $skipBaseNotAllowed
  823. * @return array
  824. */
  825. public function getAvailableCurrencyCodes($skipBaseNotAllowed = false)
  826. {
  827. $codes = $this->getData('available_currency_codes');
  828. if (null === $codes) {
  829. $codes = explode(',', $this->getConfig(Currency::XML_PATH_CURRENCY_ALLOW));
  830. // add base currency, if it is not in allowed currencies
  831. $baseCurrencyCode = $this->getBaseCurrencyCode();
  832. if (!in_array($baseCurrencyCode, $codes)) {
  833. $codes[] = $baseCurrencyCode;
  834. // save base currency code index for further usage
  835. $disallowedBaseCodeIndex = array_keys($codes);
  836. $disallowedBaseCodeIndex = array_pop($disallowedBaseCodeIndex);
  837. $this->setData('disallowed_base_currency_code_index', $disallowedBaseCodeIndex);
  838. }
  839. $this->setData('available_currency_codes', $codes);
  840. }
  841. // remove base currency code, if it is not allowed by config (optional)
  842. if ($skipBaseNotAllowed) {
  843. $disallowedBaseCodeIndex = $this->getData('disallowed_base_currency_code_index');
  844. if (null !== $disallowedBaseCodeIndex) {
  845. unset($codes[$disallowedBaseCodeIndex]);
  846. }
  847. }
  848. return $codes;
  849. }
  850. /**
  851. * Array of installed currencies for the scope
  852. *
  853. * @return array
  854. */
  855. public function getAllowedCurrencies()
  856. {
  857. return explode(',', $this->getConfig($this->_currencyInstalled));
  858. }
  859. /**
  860. * Retrieve store current currency
  861. *
  862. * @return Currency
  863. * @throws \Magento\Framework\Exception\LocalizedException
  864. */
  865. public function getCurrentCurrency()
  866. {
  867. $currency = $this->getData('current_currency');
  868. if ($currency === null) {
  869. $currency = $this->currencyFactory->create()->load($this->getCurrentCurrencyCode());
  870. $baseCurrency = $this->getBaseCurrency();
  871. if (!$baseCurrency->getRate($currency)) {
  872. $currency = $baseCurrency;
  873. $this->setCurrentCurrencyCode($baseCurrency->getCode());
  874. }
  875. $this->setData('current_currency', $currency);
  876. }
  877. return $currency;
  878. }
  879. /**
  880. * Retrieve current currency rate
  881. *
  882. * @return float
  883. * @throws \Magento\Framework\Exception\LocalizedException
  884. */
  885. public function getCurrentCurrencyRate()
  886. {
  887. return $this->getBaseCurrency()->getRate($this->getCurrentCurrency());
  888. }
  889. /**
  890. * Retrieve root category identifier
  891. *
  892. * @return int
  893. * @throws \Magento\Framework\Exception\NoSuchEntityException
  894. */
  895. public function getRootCategoryId()
  896. {
  897. if (!$this->getGroup()) {
  898. return Category::ROOT_CATEGORY_ID;
  899. }
  900. return $this->getGroup()->getRootCategoryId();
  901. }
  902. /**
  903. * Set group model for store
  904. *
  905. * @param Group $group
  906. * @return Store
  907. */
  908. public function setGroup(Group $group)
  909. {
  910. $this->setGroupId($group->getId());
  911. return $this;
  912. }
  913. /**
  914. * Retrieve group model
  915. *
  916. * @return Group|bool
  917. * @throws \Magento\Framework\Exception\NoSuchEntityException
  918. */
  919. public function getGroup()
  920. {
  921. if (null === $this->getGroupId()) {
  922. return false;
  923. }
  924. return $this->groupRepository->get($this->getGroupId());
  925. }
  926. /**
  927. * Retrieve website identifier
  928. *
  929. * @return string|int|null
  930. */
  931. public function getWebsiteId()
  932. {
  933. return $this->_getData('website_id');
  934. }
  935. /**
  936. * Reinit Stores on after save
  937. *
  938. * @deprecated 100.1.3
  939. * @return $this
  940. * @since 100.1.3
  941. */
  942. public function afterSave()
  943. {
  944. $this->_storeManager->reinitStores();
  945. if ($this->isObjectNew()) {
  946. $event = $this->_eventPrefix . '_add';
  947. } else {
  948. $event = $this->_eventPrefix . '_edit';
  949. }
  950. $store = $this;
  951. $this->getResource()->addCommitCallback(function () use ($event, $store) {
  952. $this->eventManager->dispatch($event, ['store' => $store]);
  953. });
  954. return parent::afterSave();
  955. }
  956. /**
  957. * @inheritdoc
  958. */
  959. public function setWebsiteId($websiteId)
  960. {
  961. return $this->setData('website_id', $websiteId);
  962. }
  963. /**
  964. * Retrieve group identifier
  965. *
  966. * @return string|int|null
  967. */
  968. public function getGroupId()
  969. {
  970. return $this->_getData('group_id');
  971. }
  972. /**
  973. * Retrieve store group identifier
  974. *
  975. * @return string|int|null
  976. */
  977. public function getStoreGroupId()
  978. {
  979. return $this->getGroupId();
  980. }
  981. /**
  982. * @inheritdoc
  983. */
  984. public function setStoreGroupId($storeGroupId)
  985. {
  986. return $this->setGroupId($storeGroupId);
  987. }
  988. /**
  989. * @inheritdoc
  990. * @since 101.0.0
  991. */
  992. public function getIsActive()
  993. {
  994. return $this->_getData('is_active');
  995. }
  996. /**
  997. * @inheritdoc
  998. * @since 101.0.0
  999. */
  1000. public function setIsActive($isActive)
  1001. {
  1002. return $this->setData('is_active', $isActive);
  1003. }
  1004. /**
  1005. * Retrieve default group identifier
  1006. *
  1007. * @return string|int|null
  1008. */
  1009. public function getDefaultGroupId()
  1010. {
  1011. return $this->_getData('default_group_id');
  1012. }
  1013. /**
  1014. * Check if store can be deleted
  1015. *
  1016. * @return boolean
  1017. * @throws \Magento\Framework\Exception\NoSuchEntityException
  1018. */
  1019. public function isCanDelete()
  1020. {
  1021. if (!$this->getId()) {
  1022. return false;
  1023. }
  1024. return $this->getGroup()->getStoresCount() > 1;
  1025. }
  1026. /**
  1027. * Check if store is default
  1028. *
  1029. * @return boolean
  1030. * @since 100.1.0
  1031. * @throws \Magento\Framework\Exception\NoSuchEntityException
  1032. */
  1033. public function isDefault()
  1034. {
  1035. if (!$this->getId() && $this->getWebsite() && $this->getWebsite()->getStoresCount() == 0) {
  1036. return true;
  1037. }
  1038. return $this->getGroup()->getDefaultStoreId() == $this->getId();
  1039. }
  1040. /**
  1041. * Retrieve current url for store
  1042. *
  1043. * @param bool $fromStore
  1044. *
  1045. * @return string
  1046. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  1047. * @SuppressWarnings(PHPMD.NPathComplexity)
  1048. * @throws \Magento\Framework\Exception\NoSuchEntityException
  1049. */
  1050. public function getCurrentUrl($fromStore = true)
  1051. {
  1052. $sidQueryParam = $this->_sidResolver->getSessionIdQueryParam($this->_getSession());
  1053. $requestString = $this->_url->escape(ltrim($this->_request->getRequestString(), '/'));
  1054. $storeUrl = $this->getUrl('', ['_secure' => $this->_storeManager->getStore()->isCurrentlySecure()]);
  1055. if (!filter_var($storeUrl, FILTER_VALIDATE_URL)) {
  1056. return $storeUrl;
  1057. }
  1058. $storeParsedUrl = parse_url($storeUrl);
  1059. $storeParsedQuery = [];
  1060. if (isset($storeParsedUrl['query'])) {
  1061. parse_str($storeParsedUrl['query'], $storeParsedQuery);
  1062. }
  1063. $currQuery = $this->_request->getQueryValue();
  1064. if (isset($currQuery[$sidQueryParam])
  1065. && !empty($currQuery[$sidQueryParam])
  1066. && $this->_getSession()->getSessionIdForHost($storeUrl) != $currQuery[$sidQueryParam]
  1067. ) {
  1068. unset($currQuery[$sidQueryParam]);
  1069. }
  1070. foreach ($currQuery as $key => $value) {
  1071. $storeParsedQuery[$key] = $value;
  1072. }
  1073. if (!$this->isUseStoreInUrl()) {
  1074. $storeParsedQuery['___store'] = $this->getCode();
  1075. }
  1076. if ($fromStore !== false) {
  1077. $storeParsedQuery['___from_store'] = $fromStore ===
  1078. true ? $this->_storeManager->getStore()->getCode() : $fromStore;
  1079. }
  1080. $requestStringParts = explode('?', $requestString, 2);
  1081. $requestStringPath = $requestStringParts[0];
  1082. if (isset($requestStringParts[1])) {
  1083. parse_str($requestStringParts[1], $requestString);
  1084. } else {
  1085. $requestString = [];
  1086. }
  1087. $currentUrlQueryParams = array_merge($requestString, $storeParsedQuery);
  1088. $currentUrl = $storeParsedUrl['scheme']
  1089. . '://'
  1090. . $storeParsedUrl['host']
  1091. . (isset($storeParsedUrl['port']) ? ':' . $storeParsedUrl['port'] : '')
  1092. . $storeParsedUrl['path']
  1093. . $requestStringPath
  1094. . ($currentUrlQueryParams ? '?' . http_build_query($currentUrlQueryParams) : '');
  1095. return $currentUrl;
  1096. }
  1097. /**
  1098. * Check if store is active
  1099. *
  1100. * @return boolean
  1101. */
  1102. public function isActive()
  1103. {
  1104. return (bool)$this->_getData('is_active');
  1105. }
  1106. /**
  1107. * Protect delete from non admin area
  1108. *
  1109. * @return $this
  1110. * @throws \Magento\Framework\Exception\LocalizedException
  1111. */
  1112. public function beforeDelete()
  1113. {
  1114. $this->_configDataResource->clearScopeData(ScopeInterface::SCOPE_STORES, $this->getId());
  1115. return parent::beforeDelete();
  1116. }
  1117. /**
  1118. * Rewrite in order to clear configuration cache
  1119. *
  1120. * @return $this
  1121. * @throws \Magento\Framework\Exception\NoSuchEntityException
  1122. */
  1123. public function afterDelete()
  1124. {
  1125. $store = $this;
  1126. $this->getResource()->addCommitCallback(function () use ($store) {
  1127. $this->_storeManager->reinitStores();
  1128. $this->eventManager->dispatch($this->_eventPrefix . '_delete', ['store' => $store]);
  1129. });
  1130. parent::afterDelete();
  1131. $this->_configCacheType->clean();
  1132. if ($this->getId() === $this->getGroup()->getDefaultStoreId()) {
  1133. $ids = $this->getGroup()->getStoreIds();
  1134. if (!empty($ids) && count($ids) > 1) {
  1135. unset($ids[$this->getId()]);
  1136. $defaultId = current($ids);
  1137. } else {
  1138. $defaultId = null;
  1139. }
  1140. $this->getGroup()->setDefaultStoreId($defaultId);
  1141. $this->getGroup()->save();
  1142. }
  1143. return $this;
  1144. }
  1145. /**
  1146. * Reinit and reset Config Data
  1147. *
  1148. * @return $this
  1149. */
  1150. public function resetConfig()
  1151. {
  1152. $this->_config->reinit();
  1153. $this->_dirCache = [];
  1154. $this->_baseUrlCache = [];
  1155. $this->_urlCache = [];
  1156. return $this;
  1157. }
  1158. /**
  1159. * Get/Set isReadOnly flag
  1160. *
  1161. * @param bool $value
  1162. * @return bool
  1163. */
  1164. public function isReadOnly($value = null)
  1165. {
  1166. if (null !== $value) {
  1167. $this->_isReadOnly = (bool)$value;
  1168. }
  1169. return $this->_isReadOnly;
  1170. }
  1171. /**
  1172. * Retrieve store group name
  1173. *
  1174. * @return string
  1175. * @throws \Magento\Framework\Exception\NoSuchEntityException
  1176. */
  1177. public function getFrontendName()
  1178. {
  1179. if (null === $this->_frontendName) {
  1180. $storeGroupName = (string)$this->_config
  1181. ->getValue(Information::XML_PATH_STORE_INFO_NAME, ScopeInterface::SCOPE_STORE, $this);
  1182. $this->_frontendName = !empty($storeGroupName) ? $storeGroupName : $this->getGroup()->getName();
  1183. }
  1184. return $this->_frontendName;
  1185. }
  1186. /**
  1187. * Retrieve formatted store address from config
  1188. *
  1189. * @return string
  1190. */
  1191. public function getFormattedAddress()
  1192. {
  1193. return $this->information->getFormattedAddress($this);
  1194. }
  1195. /**
  1196. * Get identities
  1197. *
  1198. * @return array
  1199. */
  1200. public function getIdentities()
  1201. {
  1202. return [self::CACHE_TAG];
  1203. }
  1204. /**
  1205. * Return Store Path
  1206. *
  1207. * @return string
  1208. */
  1209. public function getStorePath()
  1210. {
  1211. $parsedUrl = parse_url($this->getBaseUrl());
  1212. return $parsedUrl['path'] ?? '/';
  1213. }
  1214. /**
  1215. * @inheritdoc
  1216. * @since 100.1.0
  1217. */
  1218. public function getScopeType()
  1219. {
  1220. return ScopeInterface::SCOPE_STORE;
  1221. }
  1222. /**
  1223. * @inheritdoc
  1224. * @since 100.1.0
  1225. */
  1226. public function getScopeTypeName()
  1227. {
  1228. return 'Store View';
  1229. }
  1230. /**
  1231. * @inheritdoc
  1232. */
  1233. public function getExtensionAttributes()
  1234. {
  1235. return $this->_getExtensionAttributes();
  1236. }
  1237. /**
  1238. * @inheritdoc
  1239. */
  1240. public function setExtensionAttributes(
  1241. \Magento\Store\Api\Data\StoreExtensionInterface $extensionAttributes
  1242. ) {
  1243. return $this->_setExtensionAttributes($extensionAttributes);
  1244. }
  1245. /**
  1246. * Gets URL modifier.
  1247. *
  1248. * @return \Magento\Framework\Url\ModifierInterface
  1249. * @deprecated 100.1.0
  1250. */
  1251. private function getUrlModifier()
  1252. {
  1253. if ($this->urlModifier === null) {
  1254. $this->urlModifier = \Magento\Framework\App\ObjectManager::getInstance()->get(
  1255. \Magento\Framework\Url\ModifierInterface::class
  1256. );
  1257. }
  1258. return $this->urlModifier;
  1259. }
  1260. }