PageRenderTime 23ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Includes/Decorator/Plugin/Doctrine/Plugin/Multilangs/Main.php

https://github.com/inhale/core
PHP | 445 lines | 163 code | 32 blank | 250 comment | 4 complexity | c13823bc1e1577b04ffef4e04dde0069 MD5 | raw file
  1. <?php
  2. // vim: set ts=4 sw=4 sts=4 et:
  3. /**
  4. * LiteCommerce
  5. *
  6. * NOTICE OF LICENSE
  7. *
  8. * This source file is subject to the Open Software License (OSL 3.0)
  9. * that is bundled with this package in the file LICENSE.txt.
  10. * It is also available through the world-wide-web at this URL:
  11. * http://opensource.org/licenses/osl-3.0.php
  12. * If you did not receive a copy of the license and are unable to
  13. * obtain it through the world-wide-web, please send an email
  14. * to licensing@litecommerce.com so we can send you a copy immediately.
  15. *
  16. * @category LiteCommerce
  17. * @package XLite
  18. * @subpackage Includes
  19. * @author Creative Development LLC <info@cdev.ru>
  20. * @copyright Copyright (c) 2011 Creative Development LLC <info@cdev.ru>. All rights reserved
  21. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  22. * @version GIT: $Id: cbeda4699099ad0e95d728538524bfcdd0df08e2 $
  23. * @link http://www.litecommerce.com/
  24. * @see ____file_see____
  25. * @since 3.0.0
  26. */
  27. namespace Includes\Decorator\Plugin\Doctrine\Plugin\Multilangs;
  28. /**
  29. * Routines for Doctrine library
  30. *
  31. * @package XLite
  32. * @see ____class_see____
  33. * @since 3.0.0
  34. */
  35. class Main extends \Includes\Decorator\Plugin\Doctrine\Plugin\APlugin
  36. {
  37. /**
  38. * The replacement pattern
  39. */
  40. const PATTERN = '/(.*)(\}.*)/Ss';
  41. /**
  42. * List of <file, code> pairs (code replacements)
  43. *
  44. * @var array
  45. * @access protected
  46. * @see ____var_see____
  47. * @since 3.0.0
  48. */
  49. protected $replacements = array();
  50. /**
  51. * Autogenerated "translate" property
  52. *
  53. * @var string
  54. * @access protected
  55. * @see ____var_see____
  56. * @since 3.0.0
  57. */
  58. protected $translationTemplate = <<<'CODE'
  59. /**
  60. * Translations (relation). AUTOGENERATED
  61. *
  62. * @var \Doctrine\Common\Collections\ArrayCollection
  63. * @access protected
  64. * @see ____func_see____
  65. * @since 3.0.0
  66. *
  67. * @OneToMany (targetEntity="____TRANSLATION_CLASS____", mappedBy="owner", cascade={"persist","remove"})
  68. */
  69. protected $translations;
  70. CODE;
  71. /**
  72. * Autogenerated getter
  73. *
  74. * @var string
  75. * @access protected
  76. * @see ____var_see____
  77. * @since 3.0.0
  78. */
  79. protected $getTemplate = <<<'CODE'
  80. /**
  81. * Translation getter. AUTOGENERATED
  82. *
  83. * @return string
  84. * @access public
  85. * @see ____func_see____
  86. * @since 3.0.0
  87. */
  88. public function ____GETTER____()
  89. {
  90. return $this->getSoftTranslation()->____GETTER____();
  91. }
  92. CODE;
  93. /**
  94. * Autogenerated setter
  95. *
  96. * @var string
  97. * @access protected
  98. * @see ____var_see____
  99. * @since 3.0.0
  100. */
  101. protected $setTemplate = <<<'CODE'
  102. /**
  103. * Translation setter. AUTOGENERATED
  104. *
  105. * @param string $value value to set
  106. *
  107. * @return void
  108. * @access public
  109. * @see ____func_see____
  110. * @since 3.0.0
  111. */
  112. public function ____SETTER____($value)
  113. {
  114. return $this->getTranslation($this->editLanguage)->____SETTER____($value);
  115. }
  116. CODE;
  117. /**
  118. * Autogenerated "owner" property
  119. *
  120. * @var string
  121. * @access protected
  122. * @see ____var_see____
  123. * @since 3.0.0
  124. */
  125. protected $ownerTemplate = <<<'CODE'
  126. /**
  127. * Translation owner (relation). AUTOGENERATED
  128. *
  129. * @var ____OWNER_CLASS____
  130. * @access protected
  131. *
  132. * @ManyToOne (targetEntity="____MAIN_CLASS____", inversedBy="translations")
  133. * @JoinColumn (name="id", referencedColumnName="____MAIN_CLASS_ID____")
  134. */
  135. protected $owner;
  136. CODE;
  137. /**
  138. * Return list of classes with multilanguage support
  139. *
  140. * @return array
  141. * @access protected
  142. * @see ____func_see____
  143. * @since 3.0.0
  144. */
  145. protected function getMultilangModelClasses()
  146. {
  147. return static::getClassesTree()->findByCallback(array($this, 'filterByMultilangParent'));
  148. }
  149. /**
  150. * Remove leading slashes
  151. *
  152. * @param string $name class name
  153. *
  154. * @return string
  155. * @access protected
  156. * @see ____func_see____
  157. * @since 3.0.0
  158. */
  159. protected function prepareClassName($name)
  160. {
  161. return \Includes\Utils\Converter::trimLeadingChars($name, '\\');
  162. }
  163. /**
  164. * Get metadata for a class
  165. *
  166. * @param string $name class name
  167. *
  168. * @return \Doctrine\ORM\Mapping\ClassMetadata
  169. * @access protected
  170. * @see ____func_see____
  171. * @since 3.0.0
  172. */
  173. protected function getClassMetadata($name)
  174. {
  175. return \Includes\Utils\ArrayManager::searchInObjectsArray(
  176. \Includes\Decorator\Plugin\Doctrine\Utils\EntityManager::getAllMetadata(),
  177. 'name',
  178. $this->prepareClassName($name)
  179. );
  180. }
  181. /**
  182. * Substitute entries in code template
  183. *
  184. * @param string $template template to prepare
  185. * @param array $entries list of <entry, value> pairs
  186. *
  187. * @return string
  188. * @access protected
  189. * @see ____func_see____
  190. * @since 3.0.0
  191. */
  192. protected function substituteTemplate($template, array $entries)
  193. {
  194. return str_replace(array_keys($entries), $entries, $this->{$template . 'Template'});
  195. }
  196. /**
  197. * Check if the "translation" field is defined manually
  198. *
  199. * @param string $name class name
  200. *
  201. * @return string
  202. * @access protected
  203. * @see ____func_see____
  204. * @since 3.0.0
  205. */
  206. protected function getTranslationClass($name)
  207. {
  208. $class = null;
  209. $data = $this->getClassMetadata($name);
  210. if (property_exists($data, 'associationMappings') && isset($data->associationMappings['translations'])) {
  211. $class = '\\' . $data->associationMappings['translations']['targetEntity'];
  212. }
  213. return $class;
  214. }
  215. /**
  216. * Return list of the translatabe fields for a class
  217. *
  218. * @param string $name class name
  219. *
  220. * @return string
  221. * @access protected
  222. * @see ____func_see____
  223. * @since 3.0.0
  224. */
  225. protected function getTranslationFields($name)
  226. {
  227. return array_diff($this->getClassMetadata($name)->fieldNames, $name::getInternalProperties());
  228. }
  229. /**
  230. * Return default name for translation class
  231. *
  232. * @param string $name main class name
  233. *
  234. * @return string
  235. * @access protected
  236. * @see ____func_see____
  237. * @since 3.0.0
  238. */
  239. protected function getDefaultTranslationClassName($name)
  240. {
  241. return $name . 'Translation';
  242. }
  243. /**
  244. * Return list of getters/setters patterns
  245. *
  246. * @return array
  247. * @access protected
  248. * @see ____func_see____
  249. * @since 3.0.0
  250. */
  251. protected function getAutogeneratedMethodsList()
  252. {
  253. return array('get' => '____GETTER____', 'set' => '____SETTER____');
  254. }
  255. /**
  256. * Add code to replace
  257. *
  258. * @param string $class class name
  259. * @param string $code code to place
  260. *
  261. * @return void
  262. * @access protected
  263. * @see ____func_see____
  264. * @since 3.0.0
  265. */
  266. protected function addReplacement($class, $code)
  267. {
  268. $file = \Includes\Decorator\Utils\Operator::getClassFilePath(static::getClassesTree()->find($class));
  269. if (!isset($this->replacements[$file])) {
  270. $this->replacements[$file] = '';
  271. }
  272. $this->replacements[$file] .= $code . "\n\n";
  273. }
  274. /**
  275. * Wrapper for the "addReplacement"
  276. *
  277. * @param string $class class name
  278. * @param string $template template to use
  279. * @param array $substitutes list of entries to substitude
  280. *
  281. * @return void
  282. * @access protected
  283. * @see ____func_see____
  284. * @since 3.0.0
  285. */
  286. protected function replace($class, $template, array $substitutes)
  287. {
  288. empty($substitutes) ?: $this->addReplacement($class, $this->substituteTemplate($template, $substitutes));
  289. }
  290. /**
  291. * Return the array of substitutes for the "translation" template
  292. *
  293. * @param string class current multilang model class name
  294. * @param string $name translation class name
  295. *
  296. * @return array
  297. * @access protected
  298. * @see ____func_see____
  299. * @since 3.0.0
  300. */
  301. protected function getTranslationSubstitutes($class, $name)
  302. {
  303. return !$this->getClassMetadata($class)->reflClass->hasProperty(
  304. 'translations'
  305. ) ? array('____TRANSLATION_CLASS____' => $this->prepareClassName($name)) : array();
  306. }
  307. /**
  308. * Return the array of substitutes for the getters/setters templates
  309. *
  310. * @param string class current multilang model class name
  311. * @param string $entry entry to substitute
  312. * @param string $method method: "get" or "set"
  313. * @param string $field name of field to get or set
  314. *
  315. * @return array
  316. * @access protected
  317. * @see ____func_see____
  318. * @since 3.0.0
  319. */
  320. protected function getMethodSubstitutes($class, $entry, $method, $field)
  321. {
  322. return !$this->getClassMetadata($class)->reflClass->hasMethod(
  323. $method .= \Includes\Utils\Converter::convertToCamelCase($field)
  324. ) ? array($entry => $method) : array();
  325. }
  326. /**
  327. * Return the array of substitutes for the "owner" template
  328. *
  329. * @param string $name main class name
  330. *
  331. * @return array
  332. * @access protected
  333. * @see ____func_see____
  334. * @since 3.0.0
  335. */
  336. protected function getOwnerSubstitutes($name)
  337. {
  338. return array(
  339. '____OWNER_CLASS____' => $name,
  340. '____MAIN_CLASS____' => $this->prepareClassName($name),
  341. '____MAIN_CLASS_ID____' => array_shift($this->getClassMetadata($name)->identifier),
  342. );
  343. }
  344. /**
  345. * Put prepared code into the files
  346. *
  347. * @return void
  348. * @access protected
  349. * @see ____func_see____
  350. * @since 3.0.0
  351. */
  352. protected function writeData()
  353. {
  354. foreach ($this->replacements as $file => $code) {
  355. \Includes\Utils\FileManager::replace($file, '$1' . $code . '$2', self::PATTERN);
  356. }
  357. }
  358. /**
  359. * Method to filter multilang classes
  360. *
  361. * @param \Includes\Decorator\DataStructure\Node\ClassInfo $node current node
  362. *
  363. * @return bool
  364. * @access public
  365. * @see ____func_see____
  366. * @since 3.0.0
  367. */
  368. public function filterByMultilangParent(\Includes\Decorator\DataStructure\Node\ClassInfo $node)
  369. {
  370. // NOTE: do not change call order: it will cause the error in production mode
  371. return $this->getClassMetadata($node->getClass()) && is_subclass_of($node->getClass(), '\XLite\Model\Base\I18n');
  372. }
  373. /**
  374. * Execute "run" hook handler
  375. *
  376. * @return void
  377. * @access public
  378. * @see ____func_see____
  379. * @since 3.0.0
  380. */
  381. public function executeHookHandlerRun()
  382. {
  383. // Children of the "Model\I18n" class
  384. foreach ($this->getMultilangModelClasses() as $node) {
  385. // Main and translation class names.
  386. // E.g. "\XLite\Model\Product" and "\XLite\Model\ProductTranslation"
  387. $main = $node->getClass();
  388. $translation = $this->getTranslationClass($main);
  389. // Add the "translation" field to the main class (if not defined manually)
  390. if (!$translation) {
  391. $translation = $this->getDefaultTranslationClassName($main);
  392. $this->replace($main, 'translation', $this->getTranslationSubstitutes($main, $translation));
  393. }
  394. // Iterate over all translatable fields
  395. foreach ($this->getTranslationFields($translation) as $field) {
  396. // Two iteartions: "getter" and "setter"
  397. foreach ($this->getAutogeneratedMethodsList() as $method => $entry) {
  398. $this->replace($main, $method, $this->getMethodSubstitutes($main, $entry, $method, $field));
  399. }
  400. }
  401. // Add the "owner" field to the main class (if not defined manually)
  402. if (!property_exists($translation, 'owner')) {
  403. $this->replace($translation, 'owner', $this->getOwnerSubstitutes($main));
  404. }
  405. }
  406. // Populate changes
  407. $this->writeData();
  408. }
  409. }