AbstractElement.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\Data\Form\Element;
  7. use Magento\Framework\Data\Form;
  8. use Magento\Framework\Data\Form\AbstractForm;
  9. use Magento\Framework\Data\Form\Element\Renderer\RendererInterface;
  10. use Magento\Framework\Escaper;
  11. /**
  12. * Data form abstract class
  13. *
  14. * @api
  15. * @author Magento Core Team <core@magentocommerce.com>
  16. * @SuppressWarnings(PHPMD.NumberOfChildren)
  17. * @since 100.0.2
  18. */
  19. abstract class AbstractElement extends AbstractForm
  20. {
  21. /**
  22. * @var string|int
  23. */
  24. protected $_id;
  25. /**
  26. * @var string
  27. */
  28. protected $_type;
  29. /**
  30. * @var Form
  31. */
  32. protected $_form;
  33. /**
  34. * @var array
  35. */
  36. protected $_elements;
  37. /**
  38. * @var RendererInterface
  39. */
  40. protected $_renderer;
  41. /**
  42. * Shows whether current element belongs to Basic or Advanced form layout
  43. *
  44. * @var bool
  45. */
  46. protected $_advanced = false;
  47. /**
  48. * @var Escaper
  49. */
  50. protected $_escaper;
  51. /**
  52. * Lock html attribute
  53. *
  54. * @var string
  55. */
  56. private $lockHtmlAttribute = 'data-locked';
  57. /**
  58. * @param Factory $factoryElement
  59. * @param CollectionFactory $factoryCollection
  60. * @param Escaper $escaper
  61. * @param array $data
  62. */
  63. public function __construct(
  64. Factory $factoryElement,
  65. CollectionFactory $factoryCollection,
  66. Escaper $escaper,
  67. $data = []
  68. ) {
  69. $this->_escaper = $escaper;
  70. parent::__construct($factoryElement, $factoryCollection, $data);
  71. $this->_renderer = \Magento\Framework\Data\Form::getElementRenderer();
  72. }
  73. /**
  74. * Add form element
  75. *
  76. * @param AbstractElement $element
  77. * @param bool $after
  78. * @return Form
  79. */
  80. public function addElement(AbstractElement $element, $after = false)
  81. {
  82. if ($this->getForm()) {
  83. $this->getForm()->checkElementId($element->getId());
  84. $this->getForm()->addElementToCollection($element);
  85. }
  86. parent::addElement($element, $after);
  87. return $this;
  88. }
  89. /**
  90. * Shows whether current element belongs to Basic or Advanced form layout
  91. *
  92. * @return bool
  93. */
  94. public function isAdvanced()
  95. {
  96. return $this->_advanced;
  97. }
  98. /**
  99. * Set _advanced layout property
  100. *
  101. * @param bool $advanced
  102. * @return $this
  103. */
  104. public function setAdvanced($advanced)
  105. {
  106. $this->_advanced = $advanced;
  107. return $this;
  108. }
  109. /**
  110. * Get id.
  111. *
  112. * @return string|int
  113. */
  114. public function getId()
  115. {
  116. return $this->_id;
  117. }
  118. /**
  119. * Get type.
  120. *
  121. * @return string
  122. */
  123. public function getType()
  124. {
  125. return $this->_type;
  126. }
  127. /**
  128. * Get form
  129. *
  130. * @return Form
  131. */
  132. public function getForm()
  133. {
  134. return $this->_form;
  135. }
  136. /**
  137. * Set the Id.
  138. *
  139. * @param string|int $id
  140. * @return $this
  141. */
  142. public function setId($id)
  143. {
  144. $this->_id = $id;
  145. $this->setData('html_id', $id);
  146. return $this;
  147. }
  148. /**
  149. * Get the Html Id.
  150. *
  151. * @return string
  152. */
  153. public function getHtmlId()
  154. {
  155. return $this->_escaper->escapeHtml(
  156. $this->getForm()->getHtmlIdPrefix() .
  157. $this->getData('html_id') .
  158. $this->getForm()->getHtmlIdSuffix()
  159. );
  160. }
  161. /**
  162. * Get the name.
  163. *
  164. * @return mixed
  165. */
  166. public function getName()
  167. {
  168. $name = $this->_escaper->escapeHtml($this->getData('name'));
  169. if ($suffix = $this->getForm()->getFieldNameSuffix()) {
  170. $name = $this->getForm()->addSuffixToName($name, $suffix);
  171. }
  172. return $name;
  173. }
  174. /**
  175. * Set the type.
  176. *
  177. * @param string $type
  178. * @return $this
  179. */
  180. public function setType($type)
  181. {
  182. $this->_type = $type;
  183. $this->setData('type', $type);
  184. return $this;
  185. }
  186. /**
  187. * Set form.
  188. *
  189. * @param AbstractForm $form
  190. * @return $this
  191. */
  192. public function setForm($form)
  193. {
  194. $this->_form = $form;
  195. return $this;
  196. }
  197. /**
  198. * Remove field
  199. *
  200. * @param string $elementId
  201. * @return AbstractForm
  202. */
  203. public function removeField($elementId)
  204. {
  205. $this->getForm()->removeField($elementId);
  206. return parent::removeField($elementId);
  207. }
  208. /**
  209. * Return the attributes for Html.
  210. *
  211. * @return string[]
  212. */
  213. public function getHtmlAttributes()
  214. {
  215. return [
  216. 'type',
  217. 'title',
  218. 'class',
  219. 'style',
  220. 'onclick',
  221. 'onchange',
  222. 'disabled',
  223. 'readonly',
  224. 'autocomplete',
  225. 'tabindex',
  226. 'placeholder',
  227. 'data-form-part',
  228. 'data-role',
  229. 'data-action',
  230. 'checked',
  231. ];
  232. }
  233. /**
  234. * Add a class.
  235. *
  236. * @param string $class
  237. * @return $this
  238. */
  239. public function addClass($class)
  240. {
  241. $oldClass = $this->getClass();
  242. $this->setClass($oldClass . ' ' . $class);
  243. return $this;
  244. }
  245. /**
  246. * Remove CSS class
  247. *
  248. * @param string $class
  249. * @return $this
  250. */
  251. public function removeClass($class)
  252. {
  253. $classes = array_unique(explode(' ', $this->getClass()));
  254. if (false !== ($key = array_search($class, $classes))) {
  255. unset($classes[$key]);
  256. }
  257. $this->setClass(implode(' ', $classes));
  258. return $this;
  259. }
  260. /**
  261. * Escape a string's contents.
  262. *
  263. * @param string $string
  264. * @return string
  265. */
  266. protected function _escape($string)
  267. {
  268. return htmlspecialchars($string, ENT_COMPAT);
  269. }
  270. /**
  271. * Return the escaped value of the element specified by the given index.
  272. *
  273. * @param null|int|string $index
  274. * @return string
  275. */
  276. public function getEscapedValue($index = null)
  277. {
  278. $value = $this->getValue($index);
  279. if ($filter = $this->getValueFilter()) {
  280. $value = $filter->filter($value);
  281. }
  282. return $this->_escape($value);
  283. }
  284. /**
  285. * Set the renderer.
  286. *
  287. * @param RendererInterface $renderer
  288. * @return $this
  289. */
  290. public function setRenderer(RendererInterface $renderer)
  291. {
  292. $this->_renderer = $renderer;
  293. return $this;
  294. }
  295. /**
  296. * Get the renderer.
  297. *
  298. * @return RendererInterface
  299. */
  300. public function getRenderer()
  301. {
  302. return $this->_renderer;
  303. }
  304. /**
  305. * Get Ui Id.
  306. *
  307. * @param null|string $suffix
  308. * @return string
  309. */
  310. protected function _getUiId($suffix = null)
  311. {
  312. if ($this->_renderer instanceof \Magento\Framework\View\Element\AbstractBlock) {
  313. return $this->_renderer->getUiId($this->getType(), $this->getName(), $suffix);
  314. } else {
  315. return ' data-ui-id="form-element-' . $this->_escaper->escapeHtml($this->getName()) . ($suffix ?: '') . '"';
  316. }
  317. }
  318. /**
  319. * Get the Html for the element.
  320. *
  321. * @return string
  322. */
  323. public function getElementHtml()
  324. {
  325. $html = '';
  326. $htmlId = $this->getHtmlId();
  327. $beforeElementHtml = $this->getBeforeElementHtml();
  328. if ($beforeElementHtml) {
  329. $html .= '<label class="addbefore" for="' . $htmlId . '">' . $beforeElementHtml . '</label>';
  330. }
  331. if (is_array($this->getValue())) {
  332. foreach ($this->getValue() as $value) {
  333. $html .= $this->getHtmlForInputByValue($this->_escape($value));
  334. }
  335. } else {
  336. $html .= $this->getHtmlForInputByValue($this->getEscapedValue());
  337. }
  338. $afterElementJs = $this->getAfterElementJs();
  339. if ($afterElementJs) {
  340. $html .= $afterElementJs;
  341. }
  342. $afterElementHtml = $this->getAfterElementHtml();
  343. if ($afterElementHtml) {
  344. $html .= '<label class="addafter" for="' . $htmlId . '">' . $afterElementHtml . '</label>';
  345. }
  346. return $html;
  347. }
  348. /**
  349. * Get the before element html.
  350. *
  351. * @return mixed
  352. */
  353. public function getBeforeElementHtml()
  354. {
  355. return $this->getData('before_element_html');
  356. }
  357. /**
  358. * Get the after element html.
  359. *
  360. * @return mixed
  361. */
  362. public function getAfterElementHtml()
  363. {
  364. return $this->getData('after_element_html');
  365. }
  366. /**
  367. * Get the after element Javascript.
  368. *
  369. * @return mixed
  370. */
  371. public function getAfterElementJs()
  372. {
  373. return $this->getData('after_element_js');
  374. }
  375. /**
  376. * Render HTML for element's label
  377. *
  378. * @param string $idSuffix
  379. * @param string $scopeLabel
  380. * @return string
  381. */
  382. public function getLabelHtml($idSuffix = '', $scopeLabel = '')
  383. {
  384. $scopeLabel = $scopeLabel ? ' data-config-scope="' . $scopeLabel . '"' : '';
  385. if ($this->getLabel() !== null) {
  386. $html = '<label class="label admin__field-label" for="' .
  387. $this->getHtmlId() . $idSuffix . '"' . $this->_getUiId(
  388. 'label'
  389. ) . '><span' . $scopeLabel . '>' . $this->_escape(
  390. $this->getLabel()
  391. ) . '</span></label>' . "\n";
  392. } else {
  393. $html = '';
  394. }
  395. return $html;
  396. }
  397. /**
  398. * Get the default html.
  399. *
  400. * @return mixed
  401. */
  402. public function getDefaultHtml()
  403. {
  404. $html = $this->getData('default_html');
  405. if ($html === null) {
  406. $html = $this->getNoSpan() === true ? '' : '<div class="admin__field">' . "\n";
  407. $html .= $this->getLabelHtml();
  408. $html .= $this->getElementHtml();
  409. $html .= $this->getNoSpan() === true ? '' : '</div>' . "\n";
  410. }
  411. return $html;
  412. }
  413. /**
  414. * Get the html.
  415. *
  416. * @return mixed
  417. */
  418. public function getHtml()
  419. {
  420. if ($this->getRequired()) {
  421. $this->addClass('required-entry _required');
  422. }
  423. if ($this->_renderer) {
  424. $html = $this->_renderer->render($this);
  425. } else {
  426. $html = $this->getDefaultHtml();
  427. }
  428. return $html;
  429. }
  430. /**
  431. * Get the html.
  432. *
  433. * @return mixed
  434. */
  435. public function toHtml()
  436. {
  437. return $this->getHtml();
  438. }
  439. /**
  440. * Serialize the element.
  441. *
  442. * @param string[] $attributes
  443. * @param string $valueSeparator
  444. * @param string $fieldSeparator
  445. * @param string $quote
  446. * @return string
  447. */
  448. public function serialize($attributes = [], $valueSeparator = '=', $fieldSeparator = ' ', $quote = '"')
  449. {
  450. if ($this->isLocked() && !empty($attributes)) {
  451. $attributes[] = $this->lockHtmlAttribute;
  452. }
  453. if (in_array('disabled', $attributes) && !empty($this->_data['disabled'])) {
  454. $this->_data['disabled'] = 'disabled';
  455. } else {
  456. unset($this->_data['disabled']);
  457. }
  458. if (in_array('checked', $attributes) && !empty($this->_data['checked'])) {
  459. $this->_data['checked'] = 'checked';
  460. } else {
  461. unset($this->_data['checked']);
  462. }
  463. return parent::serialize($attributes, $valueSeparator, $fieldSeparator, $quote);
  464. }
  465. /**
  466. * Indicates the elements readonly status.
  467. *
  468. * @return mixed
  469. */
  470. public function getReadonly()
  471. {
  472. if ($this->hasData('readonly_disabled')) {
  473. return $this->_getData('readonly_disabled');
  474. }
  475. return $this->_getData('readonly');
  476. }
  477. /**
  478. * Get the container Id.
  479. *
  480. * @return mixed
  481. */
  482. public function getHtmlContainerId()
  483. {
  484. if ($this->hasData('container_id')) {
  485. return $this->getData('container_id');
  486. } elseif ($idPrefix = $this->getForm()->getFieldContainerIdPrefix()) {
  487. return $idPrefix . $this->getId();
  488. }
  489. return '';
  490. }
  491. /**
  492. * Add specified values to element values
  493. *
  494. * @param string|int|array $values
  495. * @param bool $overwrite
  496. * @return $this
  497. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  498. */
  499. public function addElementValues($values, $overwrite = false)
  500. {
  501. if (empty($values) || is_string($values) && trim($values) == '') {
  502. return $this;
  503. }
  504. if (!is_array($values)) {
  505. $values = $this->_escaper->escapeHtml(trim($values));
  506. $values = [$values => $values];
  507. }
  508. $elementValues = $this->getValues();
  509. if (!empty($elementValues)) {
  510. foreach ($values as $key => $value) {
  511. if (isset($elementValues[$key]) && $overwrite || !isset($elementValues[$key])) {
  512. $elementValues[$key] = $this->_escaper->escapeHtml($value);
  513. }
  514. }
  515. $values = $elementValues;
  516. }
  517. $this->setValues($values);
  518. return $this;
  519. }
  520. /**
  521. * Lock element
  522. *
  523. * @return void
  524. */
  525. public function lock()
  526. {
  527. $this->setData($this->lockHtmlAttribute, 1);
  528. }
  529. /**
  530. * Is element locked
  531. *
  532. * @return bool
  533. */
  534. public function isLocked()
  535. {
  536. return $this->getData($this->lockHtmlAttribute) == 1;
  537. }
  538. /**
  539. * Get input html by sting value.
  540. *
  541. * @param string|null $value
  542. *
  543. * @return string
  544. */
  545. private function getHtmlForInputByValue($value)
  546. {
  547. return '<input id="' . $this->getHtmlId() . '" name="' . $this->getName() . '" ' . $this->_getUiId()
  548. . ' value="' . $value . '" ' . $this->serialize($this->getHtmlAttributes()) . '/>';
  549. }
  550. }