src/Diplix/KMGBundle/DataTables/DataTablesHelper.php line 273

Open in your IDE?
  1. <?php
  2. namespace Diplix\KMGBundle\DataTables;
  3. use Diplix\KMGBundle\DataTables\Expr\ExprStub;
  4. use Doctrine\ORM\Query\Expr\Andx;
  5. use Doctrine\ORM\Query\Expr\Orx;
  6. use Doctrine\ORM\QueryBuilder;
  7. use Symfony\Component\HttpFoundation\Request;
  8. /**
  9.  * (c) C.Schmidhuber, Version Nov2018
  10.  * Class DataTablesHelper
  11.  * @package Diplix\KMGBundle\DataTables
  12.  */
  13. class DataTablesHelper
  14. {
  15.     const T_RAW "";
  16.     const T_BUTTONS "action_buttons";
  17.     const T_SELECTOR "selectorcheckbox";
  18.     const T_CSSICON "cssicon";
  19.     const T_COMMENT "comment";
  20.     // the default setup for a single column
  21.     public static $defaultSetup = array(
  22.         "fieldName"=>null,  // the name of the database field
  23.         "caption"=>null,    // the head title for the html table
  24.         "virtual"=>false,   // set to true to exclude from default filter handling
  25.         "searchable"=>true// allow searching
  26.         "searchWithLike"=>true// use like for field-specific search
  27.         "sortable"=>true,   // allow sorting for the column
  28.         "visible"=>true,    // show/hide columns,
  29.         "footer"=>false,    // add .nofooter class to footer cell
  30.         "type"=>self::T_RAW,// display type
  31.         "headStyle"=>"",    // additional styles for the th-tag
  32.     );
  33.     /** @var array Our complete setup*/
  34.     protected $columnSetup = array(
  35.         "columns" => array(),
  36.         "ajaxUrl"=>null,
  37.         "ajaxData"=>null,
  38.         "ajaxType"=>null// GET / POST
  39.         "deferLoading"=>null,
  40.         "defaultSorting"=>null,
  41.         "searching"=>true,      // https://datatables.net/reference/option/searching
  42.         "autoCaptionPrefix"=>"th.",
  43.         "addTableClass" => "",
  44.         "addTableStyle" => "",
  45.         'stateSave' => false,
  46.         'stateContext' => null,
  47.     );
  48.     /**
  49.      * @var QueryBuilder
  50.      */
  51.     protected $queryBuilder;
  52.     protected $additionalOrX = [];
  53.     protected $additionalAndX = [];
  54.     protected $additionalParams = [];
  55.     protected $additionalOrdering = [];
  56.     protected $primaryOrdering = [];
  57.     /**
  58.      * DataTablesHelper constructor.
  59.      * @param array $columSetup see self::$defaultSetup for parameters
  60.      * @throws \Exception
  61.      */
  62.     public function __construct(array $columnSetup,$options=array())
  63.     {
  64.         // take column settings
  65.         foreach ($columnSetup as $idx=>$arr)
  66.         {
  67.             // check for unknown fields
  68.             if (count(array_diff_key($arr,self::$defaultSetup))>0)
  69.             {
  70.                 throw new \Exception(sprintf("columnSetup[%d] includes unknown fields",$idx));
  71.             }
  72.             // diverging default values for special types
  73.             if (isset($arr["type"]))
  74.             {
  75.                 if ($arr["type"]==self::T_BUTTONS$arr array_merge(array("sortable"=>false,"searchable"=>false),$arr);
  76.                 if ($arr["type"]==self::T_SELECTOR$arr array_merge(array("sortable"=>false,"searchable"=>false),$arr);
  77.                 if ($arr["type"]==self::T_COMMENT)  $arr array_merge(array("searchable"=>false),$arr);
  78.             }
  79.             // fill missing fields with default values
  80.             $this->columnSetup["columns"][$idx] = array_merge(self::$defaultSetup,$arr);
  81.         }
  82.         // take other settings
  83.         if (count($options)>0)
  84.         foreach ($options as $k=>$o)
  85.         {
  86.             if (array_key_exists($k,$this->columnSetup))
  87.             {
  88.                 $this->columnSetup[$k] = $o;
  89.             }
  90.             else
  91.             {
  92.                 throw new \Exception(sprintf("Unknown parameter: %s",$k));
  93.             }
  94.         }
  95.     }
  96.     public function setQueryBuilder(QueryBuilder $queryBuilder)
  97.     {
  98.         $this->queryBuilder $queryBuilder;
  99.     }
  100.     public function processRequest(Request $request)
  101.     {
  102.         $req array_merge($request->query->all() , $request->request->all());
  103.         $this->addRequestParametersToDbQuery($this->queryBuilder,$this->columnSetup["columns"],$req);
  104.     }
  105.     public function getColumnSetup()
  106.     {
  107.         return $this->columnSetup;
  108.     }
  109.     protected function makeCaptionFromFieldName($fieldName)
  110.     {
  111.         $caption "";
  112.         // remove entity reference part before the dot
  113.         $dp strrpos($fieldName,".");
  114.         if ($dp!==false$fieldName substr($fieldName,$dp+1);
  115.         // transform
  116.         $len strlen($fieldName);
  117.             for ($i 0$i $len; ++$i) {
  118.                 if (ctype_upper($fieldName[$i])) {
  119.                     $caption .= ' '.$fieldName[$i];
  120.                 } else {
  121.                     $caption .= strtolower($fieldName[$i]);
  122.                 }
  123.             }
  124.         return ucfirst($caption);
  125.     }
  126.     public function addOr($expr,$params=[])
  127.     {
  128.         if (!is_array($expr)) $expr = [ $expr ];
  129.         foreach ($expr as $x)
  130.         {
  131.             $this->additionalOrX[]= $x;
  132.         }
  133.         $this->additionalParams array_merge($this->additionalParams,$params);
  134.     }
  135.     public function addAnd($expr,$params=[])
  136.     {
  137.         if (!is_array($expr)) $expr = [ $expr ];
  138.         foreach ($expr as $x)
  139.         {
  140.             $this->additionalAndX[]= $x;
  141.         }
  142.         $this->additionalParams array_merge($this->additionalParams,$params);
  143.     }
  144.     public function addOrderBy($expr,$order)
  145.     {
  146.         $this->additionalOrdering[$expr] = $order;
  147.     }
  148.     public function addPrimaryOrderBy($expr,$order)
  149.     {
  150.         $this->primaryOrdering[$expr] = $order;
  151.     }
  152.     /**
  153.      * @param \Doctrine\ORM\QueryBuilder $cb
  154.      * @param $knownColumns array
  155.      * @param $req array array containing the request parameters ($request->query->all())
  156.      */
  157.     protected function addRequestParametersToDbQuery(\Doctrine\ORM\QueryBuilder $cb$knownColumns$req )
  158.     {
  159.         $dtColumns = (array)$req['columns'];    // the columns known to datatables js
  160.         $params = array();
  161.         // filtering (global search)
  162.         $orLikes = array();
  163.         if ((isset($req['search']['value']))&&(strlen(trim($req['search']['value']))>0))
  164.         {
  165.             foreach ($dtColumns as $k=>$e)
  166.             {
  167.                 if ($knownColumns[$k]["virtual"]) continue; // virtual fields have to processed manually
  168.                 if (($e["searchable"]=="true")&&($knownColumns[$k]["searchable"]))
  169.                 {
  170.                     $orLikes[] =  $cb->expr()->like($knownColumns[$k]["fieldName"], ":orlike".$k );
  171.                     $params["orlike".$k] = "%".addcslashes($req['search']['value'], "%_")."%";
  172.                 }
  173.             }
  174.         }
  175.         $orLikes2 array_merge$orLikes $this->additionalOrX  );
  176.         if (count($orLikes2)>0$cb->andWhere(new Orx($orLikes2));
  177.         // filtering (field-wise search, not taking into account the actual field type)
  178.         $andLikes = array();
  179.         foreach($dtColumns as $k=>$e)
  180.         {
  181.             if ($knownColumns[$k]["virtual"]) continue; // virtual fields have to processed manually
  182.             if (($e['searchable']=="true") && ($knownColumns[$k]["searchable"]) && (trim($e['search']['value'])!=""))
  183.             {
  184.                 if ($knownColumns[$k]["searchWithLike"])
  185.                 {
  186.                     $andLikes[] = $cb->expr()->like($knownColumns[$k]["fieldName"], ":andlike".$k );
  187.                     $params["andlike".$k] = "%".addcslashes($e['search']['value'], "%_")."%";
  188.                 }
  189.                 else
  190.                 {
  191.                 $andLikes[]= $cb->expr()->eq($knownColumns[$k]["fieldName"], ":andlike".$k);
  192.                 $params["andlike".$k] = $e['search']['value'];
  193.             }
  194.         }
  195.         }
  196.         $andLikes2 array_merge($andLikes$this->additionalAndX);
  197.         if (count($andLikes2)>0$cb->andWhere(new Andx($andLikes2));
  198.         foreach ( array_merge($params,$this->additionalParams) as $k=>$e)
  199.         {
  200.             $cb->setParameter($k,$e);
  201.         }
  202.         // ordering
  203.         foreach ($this->primaryOrdering as $expr => $order)
  204.         {
  205.             $cb->addOrderBy($expr,$order);
  206.         }
  207.         if (isset($req['order']))
  208.         {
  209.             $order = (array)$req['order'];
  210.             if (count($order)>0)
  211.             foreach ($order as $o)
  212.             if ( (array_key_exists($o["column"],$knownColumns)) && ($knownColumns[$o["column"]]["sortable"]) )
  213.             {
  214.                 if ($knownColumns[$o['column']]["virtual"]) continue; // virtual fields have to processed manually
  215.                 $cb->addOrderBy$knownColumns[$o['column']]["fieldName"] , $o['dir']);
  216.             }
  217.         }
  218.         foreach ($this->additionalOrdering as $expr => $order)
  219.         {
  220.             $cb->addOrderBy($expr,$order);
  221.         }
  222.          // paging
  223.         if (isset($req['start'])) $cb->setFirstResult($req['start']);
  224.         if ( (isset($req['length'])) && ($req['length']>0)) $cb->setMaxResults($req['length']);
  225.     }
  226.     public function getRawColumnOrder($req)
  227.     {
  228.         $raw = [];
  229.         $knownColumns $this->columnSetup["columns"];
  230.         if (isset($req['order']))
  231.         {
  232.             $order = (array)$req['order'];
  233.             if (count($order) > 0)
  234.                 foreach ($order as $o)
  235.                     if ((array_key_exists($o["column"], $knownColumns)))
  236.                     {
  237.                         $raw[$o['column']] = $o["dir"];
  238.                     }
  239.         }
  240.         return $raw;
  241.     }
  242.     protected function getUniqueName()
  243.     {
  244.         return "param".rand(1000,9999);
  245.     }
  246.     public function addSimpleFilter($colIndex,$req,$order,$orX=[],$orderExpr,$weak=false,$includedToGlobal=true)
  247.     {
  248.         if (count($orX)>0)
  249.         {
  250.             // column specific search means the search term has to be there (AND)
  251.             if ($colIndex>=0)
  252.             {
  253.                 $column $req["columns"][$colIndex];
  254.                 $reqFilter = (isset($column["search"]["value"]) ? $column["search"]["value"] : "");
  255.                 if ($reqFilter!="")
  256.                 {
  257.                     if ($weak$reqFilter "%".addcslashes($reqFilter"%_")."%";
  258.                     $u $this->getUniqueName();
  259.                     $parts = [];
  260.                     /** @var ExprStub[] $orX  */
  261.                     for ($i=0;$i<count($orX);$i++) $parts[]= $orX[$i]->resurrect(":".$u);
  262.                     $this->addAnd(new OrX($parts), [$u=>$reqFilter]);
  263.                 }
  264.             }
  265.             if ($includedToGlobal)
  266.             {
  267.                 // global search filter just looks for matches everywhere (OR)
  268.                 $optFilter $req["search"]["value"];
  269.                 if ($optFilter!="")
  270.                 {
  271.                     if ($weak$optFilter "%".addcslashes($optFilter"%_")."%";
  272.                     $u $this->getUniqueName();
  273.                     $parts = [];
  274.                     /** @var ExprStub[] $orX  */
  275.                     for ($i=0;$i<count($orX);$i++) $parts[] = $orX[$i]->resurrect(":".$u);
  276.                     $this->addOr(new OrX($parts), [$u=>$optFilter]);
  277.                 }
  278.             }
  279.         }
  280.         if (array_key_exists($colIndex,$order))
  281.         {
  282.             $this->addOrderBy($orderExpr,$order[$colIndex]);
  283.         }
  284.     }
  285. }