php - A kind of pivot table using Symfony2 and Doctrine -


i'm developing kpi management tool application quality services symfony, doctrine , jquery.

i created 3 entities are:

  • application (id, name, ...)
  • kpi (id, name, category, ...)
  • value (application_id, kpi_id, month, value)

so value table has values like:

id_app  |     id_kpi     |     month   |     value  10      |        1       |  january-15 |   40,00  10      |        2       |  january-15 |   50,00  10      |        3       |  january-15 |   true  10      |        4       |  january-15 |   commentaire  22      |        1       |  january-15 |   40,00  22      |        2       |  january-15 |   40,00  etc... 

but need print this:

application|   month   | kpi 1 | kpi 2 | kpi 3   |     kpi 4   |  .......  10         |january-15 |  40   | 50    | true    | commentaire | .....  22         |january-15 |  40   | 40    | false   | commentaire | ..... 

i didn't manage it. don't know if conception , modeling good.

i manage format want (in mysql) query:

$sql = "select a.id, a.name, v.date,  group_concat(if(id_kpi = 400, value, null)) 'coverage',  group_concat(if(id_kpi = 401, value, null)) 'automation',  group_concat(if(id_kpi = 402, value, null)) 'new tc', group_concat(if(id_kpi = 403, value, null)) 'total tc', group_concat(if(id_kpi = 409, value, null)) 'release/month'  applications left join piqua_kpi.values v on a.id = v.id_application group a.name, v.date"; 

but didn't manage same doctrine , object can use.

here approach used create series of crosstab reports client. doing required abstracting @ least of formatting of pivot table. hope helps.

first, example of report: crosstab income report

in report, county entity (linked address of household) column, , household entities income bracket row entry. cells counts of households contacted in given time period row , column characteristics.

the controller (there 3 pieces):

public function incomeprofileaction(request $request) {     $form = $this->createform(new reportcriteriatype());     $criteria = $request->request->get('report_criteria');     $form->handlerequest($request);     if ($form->isvalid()) {         $response = new response();         $reportdata = $this->income($criteria);         $content = $this->profiler($reportdata);         $response->setcontent($content);          return $response;     }      return $this->render('manaclientbundle:statistics:report_criteria.html.twig', array(                 'form' => $form->createview(),                 'extra' => 'profile',                 'formpath' => "income_profile",                 'title' => 'report criteria',                 'criteriaheader' => 'select income profile reporting criteria',     )); }  private function income($criteria) {     $em = $this->getdoctrine()->getmanager();     $xp = $this->container->get('mana.crosstab');     $datecriteria = $xp->setdatecriteria($criteria);     $columntype = $criteria['columntype'];     $rowlabels = $em->getrepository('manaclientbundle:income')->rowlabels($datecriteria);     $collabels = $em->getrepository('manaclientbundle:' . $columntype)->collabels($datecriteria);     $data = $em->getrepository('manaclientbundle:income')->crosstabdata($datecriteria, $columntype);      $reportdata = [         'reporttitle' => 'household income',         'reportsubtitle' => 'for period ',         'criteria' => $criteria,         'rowheader' => 'income bracket',         'rowlabels' => $rowlabels,         'collabels' => $collabels,         'data' => $data,     ];      return $reportdata; }  private function profiler($reportdata) {     $xp = $this->container->get('mana.crosstab');     $profile = $xp->crosstabquery($reportdata['data'], $reportdata['rowlabels'], $reportdata['collabels']);     $reports = $this->get('reports');     $specs = $reports->getspecs($reportdata['criteria']);       return $this->renderview("manaclientbundle:statistics:profile.html.twig", ['profile' => $profile,                 'rowheader' => $reportdata['rowheader'],                 'rowlabels' => $reportdata['rowlabels'],                 'collabels' => $reportdata['collabels'],                 'reporttitle' => $reportdata['reporttitle'],                 'reportsubtitle' => $reportdata['reportsubtitle'],                 'date' => new \datetime(),                 'specs' => $specs,     ]); } 

template:

{% set start = specs.startdate|date('f, y') %} {% set end = specs.enddate|date('f, y') %} <h3 style="text-align: center;">{{ reporttitle }}</h3> <h4 style="text-align: center;">{{ reportsubtitle }} {{ start }}{% if (start != end) %} through {{ end }}{% endif %}</h4> <table class="table table-striped table-condensed">     <thead>         <tr>             <th>{{ rowheader }}                 {%- collabel in collabels -%}             <th style="text-align: right;">{{ collabel }} </th>                 {%- endfor -%}             <th style="text-align: right;">total</th>         </tr>     <tfoot>         <tr>             <td>total                 {% total in profile.total %}                 <td style="text-align: right">{{ total|number_format }}                 {% endfor %}     <tbody>         {% rowlabel in rowlabels -%}             <tr>                 <td>{{ rowlabel }}                     {%- collabel in collabels -%}                 <td style="text-align: right">                     {%- if profile[rowlabel][collabel] defined -%}                     {{ profile[rowlabel][collabel]|number_format }}{%- else -%}                     0{%- endif -%}                  {% endfor %}             <td style="text-align: right">{{ profile[rowlabel]['total']|number_format }}         </tr>         {%- endfor -%} </table>  

the crosstab service:

class crosstab {     private $em;      public function __construct(entitymanager $em) {         $this->em = $em;     }      /**      *       * @param array $data = data array      * @param array $rowlabels      * @param array $collabels      * @return array      */     public function crosstabquery($data, $rowlabels, $collabels)     {         $profile = $this->profilearray( $rowlabels, $collabels);         foreach ($data $array) {             if (!array_key_exists('total', $profile[$array['rowlabel']])) {                 $profile[$array['rowlabel']]['total'] = 0;             }             $profile[$array['rowlabel']][$array['collabel']] = $array['n'];             $profile[$array['rowlabel']]['total'] += $array['n'];         }         foreach ($profile $key => $array) {             if (!array_key_exists('total', $array)) {                 $profile[$key]['total'] = 0;             }         }         $profile['total'] = [];         foreach ($profile $row => $array) {             foreach ($array $key => $value) {                 if (!array_key_exists($key, $profile['total'])) {                     $profile['total'][$key] = 0;                 }                 $profile['total'][$key] += $value;             }         }          return $profile;      }      private function profilearray($rows, $cols)     {         $colkeys = [];         foreach ($cols $col) {             $colkeys[$col] = 0;         }         $profile = [];         foreach ($rows $row) {             $profile[$row] = $colkeys;         }          return $profile;     }      /**      *       * @param string $query      * @param array $criteria ['startmonth', 'startyear', 'endmonth', 'endyear']      * @return string      */     public function setdatecriteria($criteria)     {         $startmonth = $criteria['startmonth'];         $startyear = $criteria['startyear'];         $starttext = $startyear . '-' . $startmonth . '-' . '01';         $endmonth = $criteria['endmonth'];         $endyear = $criteria['endyear'];         $enddate = new \datetime($endmonth . '/01/' . $endyear);         $endtext = $enddate->format('y-m-t');          return "'$starttext' , '$endtext' ";      } } 

county repository:

class countyrepository extends entityrepository {     public function collabels($datecriteria)     {         $str = "select distinct cty.county county cty             join contact c on c.county_id = cty.id             c.contact_date between __date_criteria__              order county";         $sql = str_replace('__date_criteria__', $datecriteria, $str);         $conn = $this->getentitymanager()->getconnection();         $stmt = $conn->executequery($sql);         $colarray = $stmt->fetchall();         $collabels = [];         foreach ($colarray $array) {             $collabels[] = $array['county'];         }          return $collabels;     } 

income repository:

class incomerepository extends entityrepository {      public function rowlabels($datecriteria)     {         $str = "select distinct i.income              income             join household h on h.income_id = i.id             join contact c on c.household_id = h.id              c.contact_date between __date_criteria__             , i.enabled = true order i.id ";         $sql = str_replace('__date_criteria__', $datecriteria, $str);         $conn = $this->getentitymanager()->getconnection();         $stmt = $conn->executequery($sql);         $rowarray = $stmt->fetchall();         $rowlabels = [];         foreach ($rowarray $array) {             $rowlabels[] = $array['income'];         }          return $rowlabels;     }      public function crosstabdata($datecriteria, $profiletype)     {         $str = "select r.__type__ collabel, i.income rowlabel, count(distinct h.id) n " .                 "from household h " .                 "join contact c on c.household_id = h.id " .                 "left join __type__ r on r.id = c.__type___id " .                 "left join income on h.income_id = i.id " .                 "where c.contact_date between __date_criteria__ " .                 "and i.enabled = true " .                 "group collabel, rowlabel";         $sql1 = str_replace('__date_criteria__', $datecriteria, $str);         $sql = str_replace('__type__', $profiletype, $sql1);         $conn = $this->getentitymanager()->getconnection();         $stmt = $conn->executequery($sql);          return $stmt->fetchall();     }