📜 ⬆️ ⬇️

Symfony2. Universal tool for quick cooking of table lists in the administrative panel

It's about the symfony2 bundle, the first version of which I wrote more than two years ago. All this time, my colleagues and I actively used it, the bundle improved periodically. Decided to share it with the community.

Practically in any application it is required to display a tabular list of entities, there must be pagination, it is also nice to be able to sort by all fields and flexible filtering. These are the tasks that the AdminPanelBundle provided by your court solves. Of course, this is not something new - the same SonataAdminBundle provides similar functionality, but the Sonata is a monster (in the good sense of the word), with a lot of settings and dependencies, and my goal was to implement fast and flexible navigation through large table arrays.

What can a bundle do:

You can watch the demonstration here , the source code here .

Installation and Basic Configuration


As usual - we start
composer require "zk2/admin-panel-bundle:dev-master" 

The bundle uses knplabs / knp-paginator-bundle and braincrafted / bootstrap-bundle , if they are not in your application, they will be installed
')
Configure KnpPaginatorBundle
In app / AppKernel.php initialize the bundle
 // app/AppKernel.php public function registerBundles() { return array( // ... new Knp\Bundle\PaginatorBundle\KnpPaginatorBundle(), // ... ); } 


Customize BraincraftedBootstrapBundle
In app / AppKernel.php initialize the bundle
 // app/AppKernel.php public function registerBundles() { return array( // ... new Braincrafted\Bundle\BootstrapBundle\BraincraftedBootstrapBundle(), // ... ); } 

The setting is well described here ; if quick, then:

 # app/config/config.yml ....... # Assetic Configuration assetic: debug: "%kernel.debug%" use_controller: false bundles: [ ] filters: #   node less: node: /usr/bin/node #     $ whereis node node_paths: [/usr/lib/node_modules] # $ whereis node_modules apply_to: "\.less$" cssrewrite: ~ braincrafted_bootstrap: less_filter: less jquery_path: %kernel.root_dir%/../web/js/jquery-1.11.1.js #   jQuery 

Next, perform:

 php app/console braincrafted:bootstrap:install php app/console assetic:dump 


In app / AppKernel.php, we initialize the bundle, in app / config / config.yml we add the necessary settings:

 // app/AppKernel.php public function registerBundles() { return array( // ... new Zk2\Bundle\AdminPanelBundle\Zk2AdminPanelBundle(), // ... ); } 

 # app/config/config.yml ...... twig: ...... form: resources: - "Zk2AdminPanelBundle:AdminPanel:bootstrap_form_div_layout.html.twig" #     zk2_admin_panel: check_flag_super_admin: false # --  true,       "flagSuperAdmin()",    pagination_template: Zk2AdminPanelBundle:AdminPanel:pagination.html.twig # -    sortable_template: Zk2AdminPanelBundle:AdminPanel:sortable.html.twig # -        

And we load styles, icons, etc.

 php app/console asset:install web --symlink 


Using


I will demonstrate on the example of a small application "Cars" .
Classical structure - Country -> Brand -> Model
Do not judge strictly for the completed data - all "from the lantern."

The controller should inherit from Zk2 \ Bundle \ AdminPanelBundle \ AdminPanel \ AdminPanelController
Parent constructor accepts:

 namespace AppBundle\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Zk2\Bundle\AdminPanelBundle\AdminPanel\AdminPanelController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class DefaultController extends AdminPanelController { /** * Constructor */ public function __construct() { parent::__construct('AppBundle\Entity\Model','m'); } 

listAction - the main method
  /** * listAction * * @Route("/", name="model_list") * * @return renderView */ public function listAction( Request $request ) { //      //  isZk2Granded      //   app/config.yml  zk2_admin_panel.check_flag_super_admin == true, //    " " /* if ( false === $this->isZk2Granded(array('ROLE_LIST')) ) { throw new AccessDeniedException(); }*/ //     if( $this->isReset() ) { return $this->redirect( $this->generateUrl( $this->get('request')->get('_route') ) ); } //    $this->buildListFields(); //        $items = $this->getListFields(); //   $this->getEm()->buildQuery(); //         -- "m,b,c" , //       -- "b.id AS brand_id,b.name AS brand_name,m.name,m.color" //   ,        ,      //         $this->getQuery() ->select( 'b.id AS brand_id,b.name AS brand_name,c.name AS country_name,b.logo,m.id AS id,m.name,' .'m.color,m.airbag,m.sales,m.speed,m.price,m.dateView') ->leftJoin('m.brand','b') ->leftJoin('b.country','c') ; //    if( !$this->get('request')->query->has('sort') ) { $this->getQuery()->orderBy('m.id','DESC'); } //   $this->buildFilterFields(); //   $this->checkFilters(); //  KnpPaginator $pagination = $this->getPaginator(30); //       $filter_form = $this->getViewFiltersForm(); //    -  $this->initAutosum(); $autosum = $this->getSumColumns(); return $this->render('AppBundle:Model:list.html.twig', array( 'results' => $pagination, 'items' => $items, 'filter_form' => $filter_form, //        'is_new' => false, //$this->isZk2Granded(array('ROLE_NEW_ITEM')), 'autosum' => $autosum, //     (PHP::number_format),      'zkNumberFormat' => array('0','.',' '), )); } 

The construction of the table columns:

The addInList method accepts an array:

Default values ​​of the options array:


More information about the options and their use can be viewed in the source code AdminPanelBundle / Resources / views / AdminPanel / adminList.html.twig

You can pass any of your options, but then you need to redefine the adminList.html.twig template in one of the ways of overriding in Symfony and process them as you see fit:

  /** *    */ public function buildListFields() { $this ->addInList(array( 'name', //   $this->trans('Brand','messages'), //   'b', //   array( //      ,    ( b.name AS brand_name ) //      ,      // (    Model::getBrandName() ) 'method' => 'brand_name', //     ( @Route("/brand/{id}/edit", name="brand_edit") ) 'link_id' => 'brand_edit', //      ,    ( b.id AS brand_id ) //      ,      // (    Model::getBrandId() ) //  link_id ,  lid ,     ID    'lid' => 'brand_id' ), )) ->addInList(array( 'name', $this->trans('Country','messages'), 'c', array( 'method' => 'country_name', ), )) ->addInList(array( 'logo', $this->trans('Logo','messages'), 'b', array( 'sort' => false, 'style' => 'text-align:center', 'icon_path' => '/img/' ), )) ->addInList(array( 'name', $this->trans('Model','messages'), 'm', array( 'link_id' => 'model_edit', ), )) ->addInList(array( 'color', $this->trans('Color','messages'), 'm', array( 'style' => 'text-align:center' ), )) ->addInList(array( 'airbag', $this->trans('Airbag','messages'), 'm', array( 'filter' => 'yes_no', //   ""  "" 'style' => 'text-align:center' ), )) ->addInList(array( 'sales', $this->trans('Sales','messages'), 'm', array( 'autosum' => 'sales_sum', //     'style' => 'text-align:center' ), )) ->addInList(array( 'speed', $this->trans('Max speed','messages'), 'm', array( 'style' => 'text-align:center' ), )) ->addInList(array( 'price', $this->trans('Price','messages'), 'm', array( 'style' => 'text-align:center', 'zkNumberFormat' => array(2,'.',' ') ), )) ->addInList(array( 'dateView', $this->trans('Date','messages'), 'm', array( 'func' => 'dateTimeFormat', //  DateTime 'dateTimeFormat' => 'Ym-d', 'style' => 'text-align:center' ), )) ; } 

Building filters:

The addInFilter method accepts an array:

Filter Types:

  /** *   */ public function buildFilterFields() { $this ->addInFilter(array( // --  ,   'b_name', 'zk2_admin_panel_entity_filter', $this->trans('Brand','messages'), 5, 'smal_int', array( 'entity_type' => 'entity', 'entity_class' => 'AppBundle\Entity\Brand', 'property' => 'name', 'sf_query_builder' => array( //      'alias' => 'b', 'where' => 'b.id IS NOT NULL', 'order_field' => 'b.name', 'order_type' => 'ASC', ) ))) ->addInFilter(array( 'm_name', 'zk2_admin_panel_text_filter', $this->trans('Model','messages'), 5, 'light_text' )) ->addInFilter(array( //  ,  - 'm_color', 'zk2_admin_panel_choice_filter', $this->trans('Color','messages'), 5, 'smal_int', array('sf_choice' => array( 'black' => 'black', 'blue' => 'blue', 'brown' => 'brown', 'green' => 'green', 'red' => 'red', 'silver' => 'silver', 'white' => 'white', 'yellow' => 'yellow', )), )) ->addInFilter(array( 'm_airbag', 'zk2_admin_panel_boolean_filter', $this->trans('Airbag','messages'), )) ->addInFilter(array( 'm_door', 'zk2_admin_panel_text_filter', $this->trans('Number of doors','messages'), 5, 'medium_int' )) ->addInFilter(array( 'm_speed', 'zk2_admin_panel_text_filter', $this->trans('Max speed','messages'), 5, 'medium_int' )) ->addInFilter(array( 'm_prise', 'zk2_admin_panel_text_filter', $this->trans('Price','messages'), 5, 'medium_int' )) ->addInFilter(array( //    'm_dateView', 'zk2_admin_panel_date_filter', $this->trans('Date','messages'), 2 )) ; } 

Methods for forms
  /** * edit Brand Action * * @Route("/brand/{id}/edit", name="brand_edit") * * @param Request $request * @param integer $id * * @return renderView */ public function editBrandAction( Request $request, $id ) { ............ } /** * edit Action * * @Route("/model/{id}/edit", name="model_edit") * * @param Request $request * @param integer $id * * @return renderView */ public function editAction( Request $request, $id ) { ..... } } 

Well, very simple template
 # AppBundle:Model:list.html.twig {% extends "Zk2AdminPanelBundle::base.html.twig" %} {% block zk2_title %}Models list{% endblock %} {% block zk2_h %}<h1>General list</h1>{% endblock %} {% block zk2_body %} {% if filter_form %} {% include 'Zk2AdminPanelBundle:AdminPanel:adminFilter.html.twig' with { 'filter_form': filter_form, 'colspan': 2, {# -     #} 'this_path': path('model_list') } %} {% endif %} {% include 'Zk2AdminPanelBundle:AdminPanel:adminList.html.twig' with { 'items': items, 'results': results, 'Zk2NumberFormat': zkNumberFormat } %} {% if is_new %}  "" {% endif %} {% endblock %} 

Source: https://habr.com/ru/post/250039/


All Articles