SonataAdminExtension.php 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. <?php
  2. /*
  3. * This file is part of sonata-project.
  4. *
  5. * (c) 2010 Thomas Rabaix
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Sonata\AdminBundle\Twig\Extension;
  11. use Doctrine\Common\Util\ClassUtils;
  12. use Sonata\AdminBundle\Admin\FieldDescriptionInterface;
  13. use Sonata\AdminBundle\Exception\NoValueException;
  14. use Sonata\AdminBundle\Admin\Pool;
  15. use Symfony\Component\PropertyAccess\PropertyAccess;
  16. class SonataAdminExtension extends \Twig_Extension
  17. {
  18. /**
  19. * @var \Twig_Environment
  20. */
  21. protected $environment;
  22. /**
  23. * @var Pool
  24. */
  25. protected $pool;
  26. /**
  27. * @param Pool $pool
  28. */
  29. public function __construct(Pool $pool)
  30. {
  31. $this->pool = $pool;
  32. }
  33. /**
  34. * {@inheritdoc}
  35. */
  36. public function initRuntime(\Twig_Environment $environment)
  37. {
  38. $this->environment = $environment;
  39. }
  40. /**
  41. * {@inheritDoc}
  42. */
  43. public function getFilters()
  44. {
  45. return array(
  46. 'render_list_element' => new \Twig_Filter_Method($this, 'renderListElement', array('is_safe' => array('html'))),
  47. 'render_view_element' => new \Twig_Filter_Method($this, 'renderViewElement', array('is_safe' => array('html'))),
  48. 'render_view_element_compare' => new \Twig_Filter_Method($this, 'renderViewElementCompare', array('is_safe' => array('html'))),
  49. 'render_relation_element' => new \Twig_Filter_Method($this, 'renderRelationElement'),
  50. 'sonata_urlsafeid' => new \Twig_Filter_Method($this, 'getUrlsafeIdentifier'),
  51. 'sonata_xeditable_type' => new \Twig_Filter_Method($this, 'getXEditableType'),
  52. );
  53. }
  54. /**
  55. * {@inheritDoc}
  56. */
  57. public function getTokenParsers()
  58. {
  59. return array();
  60. }
  61. /**
  62. * {@inheritDoc}
  63. */
  64. public function getName()
  65. {
  66. return 'sonata_admin';
  67. }
  68. /**
  69. * @param FieldDescriptionInterface $fieldDescription
  70. * @param string $defaultTemplate
  71. *
  72. * @return \Twig_TemplateInterface
  73. */
  74. protected function getTemplate(FieldDescriptionInterface $fieldDescription, $defaultTemplate)
  75. {
  76. $templateName = $fieldDescription->getTemplate() ? : $defaultTemplate;
  77. try {
  78. $template = $this->environment->loadTemplate($templateName);
  79. } catch (\Twig_Error_Loader $e) {
  80. $template = $this->environment->loadTemplate($defaultTemplate);
  81. }
  82. return $template;
  83. }
  84. /**
  85. * render a list element from the FieldDescription
  86. *
  87. * @param mixed $object
  88. * @param FieldDescriptionInterface $fieldDescription
  89. * @param array $params
  90. *
  91. * @return string
  92. */
  93. public function renderListElement($object, FieldDescriptionInterface $fieldDescription, $params = array())
  94. {
  95. $template = $this->getTemplate($fieldDescription, $fieldDescription->getAdmin()->getTemplate('base_list_field'));
  96. return $this->output($fieldDescription, $template, array_merge($params, array(
  97. 'admin' => $fieldDescription->getAdmin(),
  98. 'object' => $object,
  99. 'value' => $this->getValueFromFieldDescription($object, $fieldDescription),
  100. 'field_description' => $fieldDescription
  101. )));
  102. }
  103. /**
  104. * @param FieldDescriptionInterface $fieldDescription
  105. * @param \Twig_TemplateInterface $template
  106. * @param array $parameters
  107. *
  108. * @return string
  109. */
  110. public function output(FieldDescriptionInterface $fieldDescription, \Twig_TemplateInterface $template, array $parameters = array())
  111. {
  112. $content = $template->render($parameters);
  113. if ($this->environment->isDebug()) {
  114. return sprintf("\n<!-- START \n fieldName: %s\n template: %s\n compiled template: %s\n -->\n%s\n<!-- END - fieldName: %s -->",
  115. $fieldDescription->getFieldName(),
  116. $fieldDescription->getTemplate(),
  117. $template->getTemplateName(),
  118. $content,
  119. $fieldDescription->getFieldName()
  120. );
  121. }
  122. return $content;
  123. }
  124. /**
  125. * return the value related to FieldDescription, if the associated object does no
  126. * exists => a temporary one is created
  127. *
  128. * @param object $object
  129. * @param FieldDescriptionInterface $fieldDescription
  130. * @param array $params
  131. *
  132. * @throws \RuntimeException
  133. *
  134. * @return mixed
  135. */
  136. public function getValueFromFieldDescription($object, FieldDescriptionInterface $fieldDescription, array $params = array())
  137. {
  138. if (isset($params['loop']) && $object instanceof \ArrayAccess) {
  139. throw new \RuntimeException('remove the loop requirement');
  140. }
  141. $value = null;
  142. try {
  143. $value = $fieldDescription->getValue($object);
  144. } catch (NoValueException $e) {
  145. if ($fieldDescription->getAssociationAdmin()) {
  146. $value = $fieldDescription->getAssociationAdmin()->getNewInstance();
  147. }
  148. }
  149. return $value;
  150. }
  151. /**
  152. * render a view element
  153. *
  154. * @param FieldDescriptionInterface $fieldDescription
  155. * @param mixed $object
  156. *
  157. * @return string
  158. */
  159. public function renderViewElement(FieldDescriptionInterface $fieldDescription, $object)
  160. {
  161. $template = $this->getTemplate($fieldDescription, 'SonataAdminBundle:CRUD:base_show_field.html.twig');
  162. try {
  163. $value = $fieldDescription->getValue($object);
  164. } catch (NoValueException $e) {
  165. $value = null;
  166. }
  167. return $this->output($fieldDescription, $template, array(
  168. 'field_description' => $fieldDescription,
  169. 'object' => $object,
  170. 'value' => $value,
  171. 'admin' => $fieldDescription->getAdmin()
  172. ));
  173. }
  174. /**
  175. * render a compared view element
  176. *
  177. * @param FieldDescriptionInterface $fieldDescription
  178. * @param mixed $old_value
  179. * @param mixed $new_value
  180. *
  181. * @return string
  182. */
  183. public function renderViewElementCompare(FieldDescriptionInterface $fieldDescription, $old_value, $new_value)
  184. {
  185. // The selected template for the comparison should not be defined by the $fieldDescription->getTemplate() for comparing text values
  186. $template = $this->environment->loadTemplate('SonataAdminBundle:CRUD:base_show_field_compare.html.twig');
  187. return $this->output($fieldDescription, $template, array(
  188. 'field_description' => $fieldDescription,
  189. 'value' => $old_value,
  190. 'value_compare' => $new_value,
  191. 'admin' => $fieldDescription->getAdmin()
  192. ));
  193. }
  194. /**
  195. * @throws \RunTimeException
  196. *
  197. * @param mixed $element
  198. * @param FieldDescriptionInterface $fieldDescription
  199. *
  200. * @return mixed
  201. */
  202. public function renderRelationElement($element, FieldDescriptionInterface $fieldDescription)
  203. {
  204. if (!is_object($element)) {
  205. return $element;
  206. }
  207. $propertyPath = $fieldDescription->getOption('associated_property');
  208. if (null === $propertyPath) {
  209. // For BC kept associated_tostring option behavior
  210. $method = $fieldDescription->getOption('associated_tostring', '__toString');
  211. if (!method_exists($element, $method)) {
  212. throw new \RuntimeException(sprintf(
  213. 'You must define an `associated_property` option or create a `%s::__toString` method to the field option %s from service %s is ',
  214. get_class($element),
  215. $fieldDescription->getName(),
  216. $fieldDescription->getAdmin()->getCode()
  217. ));
  218. }
  219. return call_user_func(array($element, $method));
  220. }
  221. return PropertyAccess::createPropertyAccessor()->getValue($element, $propertyPath);
  222. }
  223. /**
  224. * Get the identifiers as a string that is save to use in an url.
  225. *
  226. * @param object $model
  227. *
  228. * @return string string representation of the id that is save to use in an url
  229. */
  230. public function getUrlsafeIdentifier($model)
  231. {
  232. $admin = $this->pool->getAdminByClass(
  233. ClassUtils::getClass($model)
  234. );
  235. return $admin->getUrlsafeIdentifier($model);
  236. }
  237. /**
  238. * @param $type
  239. *
  240. * @return string|bool
  241. */
  242. public function getXEditableType($type)
  243. {
  244. $mapping = array(
  245. 'boolean' => 'select',
  246. 'text' => 'text',
  247. 'textarea' => 'textarea',
  248. 'email' => 'email',
  249. 'string' => 'text',
  250. 'smallint' => 'text',
  251. 'bigint' => 'text',
  252. 'integer' => 'number',
  253. 'decimal' => 'number',
  254. 'currency' => 'number',
  255. 'percent' => 'number',
  256. 'url' => 'url',
  257. );
  258. return isset($mapping[$type]) ? $mapping[$type] : false;
  259. }
  260. }