PageRenderTime 24ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/forms/GridFieldPresenter.php

http://github.com/silverstripe/sapphire
PHP | 417 lines | 176 code | 54 blank | 187 comment | 18 complexity | 8656c20e33ba64283c906bc8e2dc3e30 MD5 | raw file
Possible License(s): BSD-3-Clause, MIT, CC-BY-3.0, GPL-2.0, AGPL-1.0, LGPL-2.1
  1. <?php
  2. /**
  3. * The GridFieldPresenter is responsible for rendering and attach user behaviour
  4. * to a GridField.
  5. *
  6. * You can create a GridFieldPresenter and inject that into a GridField to
  7. * customise look and feel of GridField.
  8. *
  9. * It also have the possibility to let extensions to modify the look and feel of
  10. * the GridField if you dont want to make a fully blown GridFieldPresenter.
  11. *
  12. * In the following example we configure the GridField to sort the DataList in
  13. * the GridField by Title. This will override the sorting on the DataList.
  14. *
  15. * <code>
  16. * $presenter = new GridFieldPresenter();
  17. * $presenter->sort('Title', 'desc');
  18. * $gridField = new GridField('ExampleGrid', 'Example grid', new DataList('Page'),null, $presenter);
  19. * </code>
  20. *
  21. * Another example is to change the template for the rendering
  22. *
  23. * <code>
  24. * $presenter = new GridFieldPresenter();
  25. * $presenter->setTemplate('MyNiftyGridTemplate');
  26. * $gridField = new GridField('ExampleGrid', 'Example grid', new DataList('Page'),null, $presenter);
  27. * </code>
  28. *
  29. * There is also a possibility to add extensions to the GridPresenter. An
  30. * example is the DataGridPagination that decorates the GridField with
  31. * pagination. Look in the GridFieldPresenter::Items() and the filterList extend
  32. * and GridFieldPresenter::Footers()
  33. *
  34. * <code>
  35. * GridFieldPresenter::add_extension('GridFieldPaginator_Extension');
  36. * $presenter = new GridFieldPresenter();
  37. * // This is actually calling GridFieldPaginator_Extension::paginationLimit()
  38. * $presenter->paginationLimit(3);
  39. * $gridField = new GridField('ExampleGrid', 'Example grid', new DataList('Page'),null, $presenter);
  40. * </code>
  41. *
  42. * @see GridField
  43. * @see GridFieldPaginator
  44. * @package sapphire
  45. */
  46. class GridFieldPresenter extends ViewableData {
  47. /**
  48. * Template override
  49. *
  50. * @var string $template
  51. */
  52. protected $template = 'GridFieldPresenter';
  53. /**
  54. * Class name for each item/row
  55. *
  56. * @var string $itemClass
  57. */
  58. protected $itemClass = 'GridFieldPresenter_Item';
  59. /**
  60. * @var GridField
  61. */
  62. protected $GridField = null;
  63. /**
  64. * @var array
  65. */
  66. public $fieldCasting = array();
  67. /**
  68. * @var array
  69. */
  70. public $fieldFormatting = array();
  71. /**
  72. * List of columns and direction that the {@link GridFieldPresenter} is
  73. * sorted in.
  74. *
  75. * @var array
  76. */
  77. protected $sorting = array();
  78. /**
  79. * @param string $template
  80. */
  81. public function setTemplate($template){
  82. $this->template = $template;
  83. }
  84. /**
  85. * The name of the Field
  86. *
  87. * @return string
  88. */
  89. public function getName() {
  90. return $this->getGridField()->getName();
  91. }
  92. /**
  93. * @param GridField $GridField
  94. */
  95. public function setGridField(GridField $grid){
  96. $this->GridField = $grid;
  97. }
  98. /**
  99. * @return GridField
  100. */
  101. public function getGridField(){
  102. return $this->GridField;
  103. }
  104. /**
  105. *
  106. * @param type $extension
  107. */
  108. public static function add_extension($extension) {
  109. parent::add_extension(__CLASS__, $extension);
  110. }
  111. /**
  112. * Sort the grid by columns
  113. *
  114. * @param string $column
  115. * @param string $direction
  116. */
  117. public function sort($column, $direction = 'asc') {
  118. $this->sorting[$column] = $direction;
  119. return $this;
  120. }
  121. /**
  122. * Return an {@link ArrayList} of {@link GridField_Item} objects, suitable for display in the template.
  123. *
  124. * @return ArrayList
  125. */
  126. public function Items() {
  127. $items = new ArrayList();
  128. if($this->sorting) {
  129. $this->setSortingOnList($this->sorting);
  130. }
  131. //empty for now
  132. $list = $this->getGridField()->getList();
  133. $parameters = new stdClass();
  134. $parameters->Controller = Controller::curr();
  135. $parameters->Request = Controller::curr()->getRequest();
  136. $this->extend('filterList', $list, $parameters);
  137. if($list) {
  138. $numberOfRows = $list->count();
  139. $counter = 0;
  140. foreach($list as $item) {
  141. $itemPresenter = new $this->itemClass($item, $this);
  142. $itemPresenter->iteratorProperties($counter++, $numberOfRows);
  143. $items->push($itemPresenter);
  144. }
  145. }
  146. return $items;
  147. }
  148. /**
  149. * Get the headers or column names for this grid
  150. *
  151. * The returning array will have the format of
  152. *
  153. * <code>
  154. * array(
  155. * 'FirstName' => 'First name',
  156. * 'Description' => 'A nice description'
  157. * )
  158. * </code>
  159. *
  160. * @return ArrayList
  161. * @throws Exception
  162. */
  163. public function Headers() {
  164. if(!$this->getList()) {
  165. throw new Exception(sprintf(
  166. '%s needs an data source to be able to render the form', get_class($this->getGridField())
  167. ));
  168. }
  169. return $this->summaryFieldsToList($this->FieldList());
  170. }
  171. /**
  172. *
  173. * @return ArrayList
  174. */
  175. public function Footers() {
  176. $arrayList = new ArrayList();
  177. $footers = $this->extend('Footer');
  178. foreach($footers as $footer) {
  179. $arrayList->push($footer);
  180. }
  181. return $arrayList;
  182. }
  183. /**
  184. * @return SS_List
  185. */
  186. public function getList() {
  187. return $this->getGridField()->getList();
  188. }
  189. /**
  190. * @return string - name of model
  191. */
  192. protected function getModelClass() {
  193. return $this->getGridField()->getModelClass();
  194. }
  195. /**
  196. * Add the combined sorting on the datasource
  197. *
  198. * If the sorting isn't set in the datasource, only the latest sort
  199. * will be executed.
  200. *
  201. * @param array $sortColumns
  202. */
  203. protected function setSortingOnList(array $sortColumns) {
  204. $resultColumns = array();
  205. foreach($sortColumns as $column => $sortOrder) {
  206. $resultColumns[] = sprintf("%s %s", $column ,$sortOrder);
  207. }
  208. $sort = implode(', ', $resultColumns);
  209. $this->getList()->sort($sort);
  210. }
  211. /**
  212. * @return array
  213. */
  214. public function FieldList() {
  215. return singleton($this->getModelClass())->summaryFields();
  216. }
  217. /**
  218. * Translate the summaryFields from a model into a format that is understood
  219. * by the Form renderer
  220. *
  221. * @param array $summaryFields
  222. *
  223. * @return ArrayList
  224. */
  225. protected function summaryFieldsToList($summaryFields) {
  226. $headers = new ArrayList();
  227. if(is_array($summaryFields)) {
  228. $counter = 0;
  229. foreach ($summaryFields as $name => $title) {
  230. $data = array(
  231. 'Name' => $name,
  232. 'Title' => $title,
  233. 'IsSortable' => true,
  234. 'IsSorted' => false,
  235. 'SortedDirection' => 'asc'
  236. );
  237. if(array_key_exists($name, $this->sorting)) {
  238. $data['IsSorted'] = true;
  239. $data['SortedDirection'] = $this->sorting[$name];
  240. }
  241. $result = new ArrayData($data);
  242. $result->iteratorProperties($counter++, count($summaryFields));
  243. $headers->push($result);
  244. }
  245. }
  246. return $headers;
  247. }
  248. /**
  249. * @param array $casting
  250. */
  251. function setFieldCasting($casting) {
  252. $this->fieldCasting = $casting;
  253. }
  254. /**
  255. *
  256. * @param type $formatting
  257. */
  258. function setFieldFormatting($formatting) {
  259. $this->fieldFormatting = $formatting;
  260. }
  261. /**
  262. * @return string - html
  263. */
  264. function render(){
  265. return $this->renderWith(array($this->template));
  266. }
  267. }
  268. /**
  269. * A single record in a GridField.
  270. *
  271. * @package sapphire
  272. * @see GridField
  273. */
  274. class GridFieldPresenter_Item extends ViewableData {
  275. /**
  276. * @var Object The underlying record, usually an element of
  277. * {@link GridField->datasource()}.
  278. */
  279. protected $item;
  280. /**
  281. * @var GridFieldPresenter
  282. */
  283. protected $parent;
  284. /**
  285. * @param Object $item
  286. * @param GridFieldPresenter $parent
  287. */
  288. public function __construct($item, $parent) {
  289. $this->failover = $this->item = $item;
  290. $this->parent = $parent;
  291. parent::__construct();
  292. }
  293. /**
  294. * @return int
  295. */
  296. public function ID() {
  297. return $this->item->ID;
  298. }
  299. /**
  300. * @return type
  301. */
  302. public function Parent() {
  303. return $this->parent;
  304. }
  305. /**
  306. * @param bool $xmlSafe
  307. *
  308. * @return ArrayList
  309. */
  310. public function Fields($xmlSafe = true) {
  311. $list = $this->parent->FieldList();
  312. $counter = 0;
  313. foreach($list as $fieldName => $fieldTitle) {
  314. $value = "";
  315. // TODO Delegates that to DataList
  316. // This supports simple FieldName syntax
  317. if(strpos($fieldName,'.') === false) {
  318. $value = ($this->item->XML_val($fieldName) && $xmlSafe) ? $this->item->XML_val($fieldName) : $this->item->RAW_val($fieldName);
  319. // This support the syntax fieldName = Relation.RelatedField
  320. } else {
  321. $fieldNameParts = explode('.', $fieldName) ;
  322. $tmpItem = $this->item;
  323. for($j=0;$j<sizeof($fieldNameParts);$j++) {
  324. $relationMethod = $fieldNameParts[$j];
  325. $idField = $relationMethod . 'ID';
  326. if($j == sizeof($fieldNameParts)-1) {
  327. if($tmpItem) $value = $tmpItem->$relationMethod;
  328. } else {
  329. if($tmpItem) $tmpItem = $tmpItem->$relationMethod();
  330. }
  331. }
  332. }
  333. // casting
  334. if(array_key_exists($fieldName, $this->parent->fieldCasting)) {
  335. $value = $this->parent->getCastedValue($value, $this->parent->fieldCasting[$fieldName]);
  336. } elseif(is_object($value) && method_exists($value, 'Nice')) {
  337. $value = $value->Nice();
  338. }
  339. // formatting
  340. $item = $this->item;
  341. if(array_key_exists($fieldName, $this->parent->fieldFormatting)) {
  342. $format = str_replace('$value', "__VAL__", $this->parent->fieldFormatting[$fieldName]);
  343. $format = preg_replace('/\$([A-Za-z0-9-_]+)/','$item->$1', $format);
  344. $format = str_replace('__VAL__', '$value', $format);
  345. eval('$value = "' . $format . '";');
  346. }
  347. //escape
  348. if($escape = $this->parent->getGridField()->fieldEscape){
  349. foreach($escape as $search => $replace){
  350. $value = str_replace($search, $replace, $value);
  351. }
  352. }
  353. $arrayData = new ArrayData(array(
  354. "Name" => $fieldName,
  355. "Title" => $fieldTitle,
  356. "Value" => $value
  357. ));
  358. $arrayData->iteratorProperties($counter++, count($list));
  359. $fields[] = $arrayData;
  360. }
  361. return new ArrayList($fields);
  362. }
  363. }