Admin.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836
  1. <?php
  2. /*
  3. * This file is part of the Sonata package.
  4. *
  5. * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
  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\BaseApplicationBundle\Admin;
  11. use Symfony\Component\DependencyInjection\ContainerAware;
  12. use Symfony\Component\Form\Form;
  13. use Sonata\BaseApplicationBundle\Form\FormMapper;
  14. use Sonata\BaseApplicationBundle\Datagrid\ListMapper;
  15. use Sonata\BaseApplicationBundle\Datagrid\DatagridMapper;
  16. use Sonata\BaseApplicationBundle\Datagrid\Datagrid;
  17. abstract class Admin extends ContainerAware
  18. {
  19. protected $class;
  20. protected $list = array();
  21. protected $listFieldDescriptions = array();
  22. protected $form = array();
  23. protected $formFieldDescriptions = array();
  24. protected $filter = array();
  25. protected $filterFieldDescriptions = array();
  26. protected $maxPerPage = 25;
  27. protected $baseRouteName;
  28. protected $baseRoutePattern;
  29. protected $baseControllerName;
  30. protected $formGroups = false;
  31. /**
  32. *
  33. * @var array options to set to the form (ie, validation_groups)
  34. */
  35. protected $formOptions = array();
  36. // note : don't like this, but havn't find a better way to do it
  37. protected $configurationPool;
  38. protected $code;
  39. protected $label;
  40. protected $urls = array();
  41. protected $subject;
  42. /**
  43. * Reference the parent FieldDescription related to this admin
  44. * only set for FieldDescription which is associated to an Sub Admin instance
  45. *
  46. * FieldDescription
  47. */
  48. protected $parentFieldDescription;
  49. protected $loaded = array(
  50. 'form_fields' => false,
  51. 'form_groups' => false,
  52. 'list_fields' => false,
  53. 'filter_fields' => false,
  54. 'urls' => false,
  55. );
  56. protected $choicesCache = array();
  57. /**
  58. * return the entity manager
  59. *
  60. * @return EntityManager
  61. */
  62. abstract public function getEntityManager();
  63. abstract public function getListBuilder();
  64. abstract public function getFormBuilder();
  65. abstract public function getDatagridBuilder();
  66. abstract public function getClassMetaData();
  67. /**
  68. * This method can be overwritten to tweak the form construction, by default the form
  69. * is built by reading the FieldDescription
  70. *
  71. * @return void
  72. */
  73. protected function configureFormFields(FormMapper $form)
  74. {
  75. }
  76. protected function configureListFields(ListMapper $list)
  77. {
  78. }
  79. protected function configureDatagridFilters(DatagridMapper $filter)
  80. {
  81. }
  82. public function configureUrls()
  83. {
  84. }
  85. public function preUpdate($object)
  86. {
  87. }
  88. public function postUpdate($object)
  89. {
  90. }
  91. public function preInsert($object)
  92. {
  93. }
  94. public function postInsert($object)
  95. {
  96. }
  97. /**
  98. * build the field to use in the list view
  99. *
  100. * @return void
  101. */
  102. protected function buildListFieldDescriptions()
  103. {
  104. if ($this->loaded['list_fields']) {
  105. return;
  106. }
  107. $this->loaded['list_fields'] = true;
  108. $this->listFieldDescriptions = self::getBaseFields($this->getClassMetaData(), $this->list);
  109. // normalize field
  110. foreach ($this->listFieldDescriptions as $fieldDescription) {
  111. $this->getListBuilder()->fixFieldDescription($this, $fieldDescription);
  112. }
  113. if (!isset($this->listFieldDescriptions['_batch'])) {
  114. $fieldDescription = new FieldDescription();
  115. $fieldDescription->setOptions(array(
  116. 'label' => 'batch',
  117. 'code' => '_batch',
  118. 'type' => 'batch',
  119. ));
  120. $fieldDescription->setTemplate('SonataBaseApplicationBundle:CRUD:list__batch.twig.html');
  121. $this->listFieldDescriptions = array( '_batch' => $fieldDescription ) + $this->listFieldDescriptions;
  122. }
  123. return $this->listFieldDescriptions;
  124. }
  125. public function buildFilterFieldDescriptions()
  126. {
  127. if ($this->loaded['filter_fields']) {
  128. return;
  129. }
  130. $this->loaded['filter_fields'] = true;
  131. $this->filterFieldDescriptions = self::getBaseFields($this->getClassMetaData(), $this->filter);
  132. foreach ($this->filterFieldDescriptions as $fieldDescription) {
  133. $this->getDatagridBuilder()->fixFieldDescription($this, $fieldDescription);
  134. }
  135. }
  136. /**
  137. * Build the form's FieldDescription collection
  138. *
  139. * @return
  140. */
  141. protected function buildFormFieldDescriptions()
  142. {
  143. if ($this->loaded['form_fields']) {
  144. return;
  145. }
  146. $this->loaded['form_fields'] = true;
  147. $this->formFieldDescriptions = self::getBaseFields($this->getClassMetaData(), $this->form);
  148. foreach ($this->formFieldDescriptions as $name => &$fieldDescription) {
  149. $this->getFormBuilder()->fixFieldDescription($this, $fieldDescription);
  150. // unset the identifier field as it is not required to update an object
  151. if ($fieldDescription->isIdentifier()) {
  152. unset($this->formFieldDescriptions[$name]);
  153. }
  154. }
  155. }
  156. /**
  157. * make sure the base fields are set in the correct format
  158. *
  159. * @param $selected_fields
  160. * @return array
  161. */
  162. static public function getBaseFields($metadata, $selectedFields)
  163. {
  164. $fields = array();
  165. // make sure we works with array
  166. foreach ($selectedFields as $name => $options) {
  167. $description = new FieldDescription;
  168. if (!is_array($options)) {
  169. $name = $options;
  170. $options = array();
  171. }
  172. $description->setName($name);
  173. $description->setOptions($options);
  174. $fields[$name] = $description;
  175. }
  176. return $fields;
  177. }
  178. /**
  179. * return the baseRoutePattern used to generate the routing information
  180. *
  181. * @throws RuntimeException
  182. * @return string the baseRoutePattern used to generate the routing information
  183. */
  184. public function getBaseRoutePattern()
  185. {
  186. if (!$this->baseRoutePattern) {
  187. if (preg_match('@(Application|Bundle)\\\([A-Za-z]*)\\\([A-Za-z]*)Bundle\\\(Entity|Document)\\\([A-Za-z]*)@', $this->getClass(), $matches)) {
  188. $this->baseRoutePattern = sprintf('/%s/%s/%s',
  189. $this->urlize($matches[2], '-'),
  190. $this->urlize($matches[3], '-'),
  191. $this->urlize($matches[5], '-')
  192. );
  193. } else {
  194. throw new \RuntimeException(sprintf('Please define a default `baseRoutePattern` value for the admin class `%s`', get_class($this)));
  195. }
  196. }
  197. return $this->baseRoutePattern;
  198. }
  199. /**
  200. * return the baseRouteName used to generate the routing information
  201. *
  202. * @throws RuntimeException
  203. * @return string the baseRouteName used to generate the routing information
  204. */
  205. public function getBaseRouteName()
  206. {
  207. if (!$this->baseRouteName) {
  208. if (preg_match('@(Application|Bundle)\\\([A-Za-z]*)\\\([A-Za-z]*)Bundle\\\(Entity|Document)\\\([A-Za-z]*)@', $this->getClass(), $matches)) {
  209. $this->baseRouteName = sprintf('admin_%s_%s_%s',
  210. $this->urlize($matches[2]),
  211. $this->urlize($matches[3]),
  212. $this->urlize($matches[5])
  213. );
  214. } else {
  215. throw new \RuntimeException(sprintf('Please define a default `baseRouteName` value for the admin class `%s`', get_class($this)));
  216. }
  217. }
  218. return $this->baseRouteName;
  219. }
  220. public function urlize($word, $sep = '_')
  221. {
  222. return strtolower(preg_replace('~(?<=\\w)([A-Z])~', $sep.'$1', $word));
  223. }
  224. /**
  225. * return the class name handled by the Admin instance
  226. *
  227. * @return string the class name handled by the Admin instance
  228. */
  229. public function getClass()
  230. {
  231. return $this->class;
  232. }
  233. /**
  234. * return the list of batchs actions
  235. *
  236. * @return array the list of batchs actions
  237. */
  238. public function getBatchActions()
  239. {
  240. return array(
  241. 'delete' => 'action_delete'
  242. );
  243. }
  244. /**
  245. * return the list of available urls
  246. *
  247. * @return array the list of available urls
  248. */
  249. public function getUrls()
  250. {
  251. $this->buildUrls();
  252. return $this->urls;
  253. }
  254. public function buildUrls()
  255. {
  256. if ($this->loaded['urls']) {
  257. return;
  258. }
  259. $this->urls = array(
  260. 'list' => array(
  261. 'name' => $this->getBaseRouteName().'_list',
  262. 'pattern' => $this->getBaseRoutePattern().'/list',
  263. 'defaults' => array(
  264. '_controller' => $this->getBaseControllerName().':list'
  265. ),
  266. 'requirements' => array(),
  267. 'options' => array(),
  268. 'params' => array(),
  269. ),
  270. 'create' => array(
  271. 'name' => $this->getBaseRouteName().'_create',
  272. 'pattern' => $this->getBaseRoutePattern().'/create',
  273. 'defaults' => array(
  274. '_controller' => $this->getBaseControllerName().':create'
  275. ),
  276. 'requirements' => array(),
  277. 'options' => array(),
  278. 'params' => array(),
  279. ),
  280. 'edit' => array(
  281. 'name' => $this->getBaseRouteName().'_edit',
  282. 'pattern' => $this->getBaseRoutePattern().'/{id}/edit',
  283. 'defaults' => array(
  284. '_controller' => $this->getBaseControllerName().':edit'
  285. ),
  286. 'requirements' => array(),
  287. 'options' => array(),
  288. 'params' => array(),
  289. ),
  290. 'update' => array(
  291. 'name' => $this->getBaseRouteName().'_update',
  292. 'pattern' => $this->getBaseRoutePattern().'/update',
  293. 'defaults' => array(
  294. '_controller' => $this->getBaseControllerName().':update'
  295. ),
  296. 'requirements' => array(),
  297. 'options' => array(),
  298. 'params' => array(),
  299. ),
  300. 'batch' => array(
  301. 'name' => $this->getBaseRouteName().'_batch',
  302. 'pattern' => $this->getBaseRoutePattern().'/batch',
  303. 'defaults' => array(
  304. '_controller' => $this->getBaseControllerName().':batch'
  305. ),
  306. 'requirements' => array(),
  307. 'options' => array(),
  308. 'params' => array(),
  309. )
  310. );
  311. $this->configureUrls();
  312. }
  313. /**
  314. * return the url defined by the $name
  315. *
  316. * @param $name
  317. * @return bool
  318. */
  319. public function getUrl($name)
  320. {
  321. $urls = $this->getUrls();
  322. if (!isset($urls[$name])) {
  323. return false;
  324. }
  325. return $urls[$name];
  326. }
  327. /**
  328. * generate the url with the given $name
  329. *
  330. * @throws RuntimeException
  331. * @param $name
  332. * @param array $params
  333. *
  334. * @return return a complete url
  335. */
  336. public function generateUrl($name, $params = array())
  337. {
  338. $url = $this->getUrl($name);
  339. if (!$url) {
  340. throw new \RuntimeException(sprintf('unable to find the url `%s`', $name));
  341. }
  342. if (!is_array($params)) {
  343. $params = array();
  344. }
  345. return $this->container->get('router')->generate($url['name'], array_merge($url['params'], $params));
  346. }
  347. /**
  348. * return the list template
  349. *
  350. * @return string the list template
  351. */
  352. public function getListTemplate()
  353. {
  354. return 'SonataBaseApplicationBundle:CRUD:list.twig.html';
  355. }
  356. /**
  357. * return the edit template
  358. *
  359. * @return string the edit template
  360. */
  361. public function getEditTemplate()
  362. {
  363. return 'SonataBaseApplicationBundle:CRUD:edit.twig.html';
  364. }
  365. public function getReflectionFields()
  366. {
  367. return $this->getClassMetaData()->reflFields;
  368. }
  369. public function getNewInstance()
  370. {
  371. $class = $this->getClass();
  372. return new $class;
  373. }
  374. /**
  375. *
  376. * @return Form the base form
  377. */
  378. public function getBaseForm($object, $options = array())
  379. {
  380. return $this->getFormBuilder()->getBaseForm($object, array_merge($this->formOptions, $options));
  381. }
  382. /**
  383. *
  384. * @return Form the base form
  385. */
  386. public function getBaseDatagrid()
  387. {
  388. return new Datagrid(
  389. $this->getClass(),
  390. $this->getEntityManager()
  391. );
  392. }
  393. /**
  394. * attach an admin instance to the given FieldDescription
  395. *
  396. */
  397. public function attachAdminClass(FieldDescription $fieldDescription)
  398. {
  399. $pool = $this->getConfigurationPool();
  400. $admin = $pool->getAdminByClass($fieldDescription->getTargetEntity());
  401. if (!$admin) {
  402. throw new \RuntimeException(sprintf('You must define an Admin class for the `%s` field (targetEntity=%s)', $fieldDescription->getFieldName(), $fieldDescription->getTargetEntity()));
  403. }
  404. $fieldDescription->setAssociationAdmin($admin);
  405. }
  406. /**
  407. * return the target objet
  408. *
  409. * @param $id
  410. * @return
  411. */
  412. public function getObject($id)
  413. {
  414. return $this->getEntityManager()
  415. ->find($this->getClass(), $id);
  416. }
  417. public function buildFormGroups(Form $form)
  418. {
  419. if ($this->loaded['form_groups']) {
  420. return;
  421. }
  422. $this->loaded['form_groups'] = true;
  423. if (!$this->formGroups) {
  424. $this->formGroups = array(
  425. false => array('fields' => array_keys($this->getFormFieldDescriptions()))
  426. );
  427. }
  428. // normalize array
  429. foreach ($this->formGroups as $name => $group) {
  430. if (!isset($this->formGroups[$name]['collapsed'])) {
  431. $this->formGroups[$name]['collapsed'] = false;
  432. }
  433. }
  434. }
  435. /**
  436. * return a form depend on the given $object
  437. *
  438. * @param $object
  439. * @return Symfony\Component\Form\Form
  440. */
  441. public function getForm($object, array $options = array())
  442. {
  443. $form = $this->getBaseForm($object, $options);
  444. $mapper = new FormMapper($this->getFormBuilder(), $form, $this);
  445. foreach ($this->getFormFieldDescriptions() as $fieldDescription) {
  446. if (!$fieldDescription->getType()) {
  447. continue;
  448. }
  449. $mapper->add($fieldDescription);
  450. }
  451. $this->configureFormFields($mapper);
  452. return $form;
  453. }
  454. /**
  455. * return a list depend on the given $object
  456. *
  457. * @param $object
  458. * @return Symfony\Component\Datagrid\ListCollection
  459. */
  460. public function getList(array $options = array())
  461. {
  462. $list = $this->getListBuilder()->getBaseList($options);
  463. $mapper = new ListMapper($this->getListBuilder(), $list, $this);
  464. foreach ($this->getListFieldDescriptions() as $fieldDescription) {
  465. if (!$fieldDescription->getType()) {
  466. continue;
  467. }
  468. $mapper->add($fieldDescription);
  469. }
  470. $this->configureListFields($mapper);
  471. return $list;
  472. }
  473. /**
  474. * return a list depend on the given $object
  475. *
  476. * @param $object
  477. * @return Symfony\Component\Datagrid\Datagrid
  478. */
  479. public function getDatagrid()
  480. {
  481. $datagrid = $this->getBaseDatagrid();
  482. $datagrid->setMaxPerPage($this->maxPerPage);
  483. $datagrid->setValues($this->container->get('request')->query->all());
  484. $mapper = new DatagridMapper($this->getDatagridBuilder(), $datagrid, $this);
  485. foreach ($this->getFilterFieldDescriptions() as $fieldDescription) {
  486. if (!$fieldDescription->getType()) {
  487. continue;
  488. }
  489. $mapper->add($fieldDescription);
  490. }
  491. $this->configureDatagridFilters($mapper);
  492. return $datagrid;
  493. }
  494. /**
  495. *
  496. */
  497. public function getRootCode()
  498. {
  499. return $this->getRoot()->getCode();
  500. }
  501. /**
  502. * return the master admin
  503. *
  504. */
  505. public function getRoot()
  506. {
  507. $parentFieldDescription = $this->getParentFieldDescription();
  508. if (!$parentFieldDescription) {
  509. return $this;
  510. }
  511. return $parentFieldDescription->getAdmin()->getRoot();
  512. }
  513. public function setBaseControllerName($baseControllerName)
  514. {
  515. $this->baseControllerName = $baseControllerName;
  516. }
  517. public function getBaseControllerName()
  518. {
  519. return $this->baseControllerName;
  520. }
  521. public function setConfigurationPool($configurationPool)
  522. {
  523. $this->configurationPool = $configurationPool;
  524. }
  525. public function getConfigurationPool()
  526. {
  527. return $this->configurationPool;
  528. }
  529. public function setCode($code)
  530. {
  531. $this->code = $code;
  532. }
  533. public function getCode()
  534. {
  535. return $this->code;
  536. }
  537. public function setLabel($label)
  538. {
  539. $this->label = $label;
  540. }
  541. public function getLabel()
  542. {
  543. return $this->label;
  544. }
  545. public function setMaxPerPage($maxPerPage)
  546. {
  547. $this->maxPerPage = $maxPerPage;
  548. }
  549. public function getMaxPerPage()
  550. {
  551. return $this->maxPerPage;
  552. }
  553. public function setFormGroups($formGroups)
  554. {
  555. $this->formGroups = $formGroups;
  556. }
  557. public function getFormGroups(Form $form)
  558. {
  559. $this->buildFormGroups($form);
  560. return $this->formGroups;
  561. }
  562. public function setParentFieldDescription($parentFieldDescription)
  563. {
  564. $this->parentFieldDescription = $parentFieldDescription;
  565. }
  566. public function getParentFieldDescription()
  567. {
  568. return $this->parentFieldDescription;
  569. }
  570. public function setSubject($subject)
  571. {
  572. $this->subject = $subject;
  573. }
  574. public function getSubject()
  575. {
  576. return $this->subject;
  577. }
  578. public function getFormFieldDescriptions()
  579. {
  580. $this->buildFormFieldDescriptions();
  581. return $this->formFieldDescriptions;
  582. }
  583. public function getFormFieldDescription($name) {
  584. return $this->hasFormFieldDescription($name) ? $this->formFieldDescriptions[$name] : null;
  585. }
  586. public function hasFormFieldDescription($name)
  587. {
  588. $this->buildFormFieldDescriptions();
  589. return array_key_exists($name, $this->formFieldDescriptions) ? true : false;
  590. }
  591. public function addFormFieldDescription($name, FieldDescription $fieldDescription)
  592. {
  593. $this->formFieldDescriptions[$name] = $fieldDescription;
  594. }
  595. public function removeFormFieldDescription($name)
  596. {
  597. unset($this->formFieldDescriptions[$name]);
  598. }
  599. public function getListFieldDescriptions()
  600. {
  601. $this->buildListFieldDescriptions();
  602. return $this->listFieldDescriptions;
  603. }
  604. public function getListFieldDescription($name) {
  605. return $this->hasListFieldDescription($name) ? $this->listFieldDescriptions[$name] : null;
  606. }
  607. public function hasListFieldDescription($name)
  608. {
  609. $this->buildListFieldDescriptions();
  610. return array_key_exists($name, $this->listFieldDescriptions) ? true : false;
  611. }
  612. public function addListFieldDescription($name, FieldDescription $fieldDescription)
  613. {
  614. $this->listFieldDescriptions[$name] = $fieldDescription;
  615. }
  616. public function removeListFieldDescription($name)
  617. {
  618. unset($this->listFieldDescriptions[$name]);
  619. }
  620. public function getFilterFieldDescription($name) {
  621. return $this->hasFilterFieldDescription($name) ? $this->filterFieldDescriptions[$name] : null;
  622. }
  623. public function hasFilterFieldDescription($name)
  624. {
  625. $this->buildFilterFieldDescriptions();
  626. return array_key_exists($name, $this->filterFieldDescriptions) ? true : false;
  627. }
  628. public function addFilterFieldDescription($name, FieldDescription $fieldDescription)
  629. {
  630. $this->filterFieldDescriptions[$name] = $fieldDescription;
  631. }
  632. public function removeFilterFieldDescription($name)
  633. {
  634. unset($this->filterFieldDescriptions[$name]);
  635. }
  636. public function getFilterFieldDescriptions()
  637. {
  638. $this->buildFilterFieldDescriptions();
  639. return $this->filterFieldDescriptions;
  640. }
  641. }