一种使用 Symfony2 和 Doctrine 的枢轴 table
A kind of pivot table using Symfony2 and Doctrine
我正在使用 Symfony、Doctrine 和 jQuery 开发用于应用程序质量服务的 KPI 管理工具。
我创建了三个实体,它们是:
- 申请(ID、姓名...)
- KPI(ID、名称、类别...)
- 值(Application_ID、KPI_ID、月、值)
所以我的值 table 有一些值,例如:
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...
但我需要这样打印:
Application| Month | KPI 1 | KPI 2 | KPI 3 | KPI 4 | .......
10 |January-15 | 40 | 50 | true | commentaire | .....
22 |January-15 | 40 | 40 | false | commentaire | .....
我没有做到。不知道我的构思和造型好不好
我设法通过此查询获得了我想要的格式(在 MySQL 中):
$sql = "select a.id, a.name, v.date,
GROUP_CONCAT(if(id_kpi = 400, value, NULL)) AS 'Coverage',
GROUP_CONCAT(if(id_kpi = 401, value, NULL)) AS 'Automation',
GROUP_CONCAT(if(id_kpi = 402, value, NULL)) AS 'NEW TC',
GROUP_CONCAT(if(id_kpi = 403, value, NULL)) AS 'TOTAL TC',
GROUP_CONCAT(if(id_kpi = 409, value, NULL)) AS 'Release/Month'
from applications as a
left join piqua_kpi.values as v on a.id = v.id_application
group by a.name, v.date";
但我没能对 Doctrine 做同样的事情并得到一个我可以使用的对象。
这是我用来为客户创建一系列交叉表报告的方法。这样做需要至少抽象一些数据透视表 table 的格式。希望对你有帮助。
首先是一个报表示例:
在此报告中,县实体(链接到家庭地址)是列,家庭实体收入等级是行条目。单元格是在给定时间段内联系的具有行和列特征的家庭的计数。
控制器(一共三块):
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 the 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,
]);
}
模板:
{% 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 }}
{%- for colLabel in colLabels -%}
<th style="text-align: right;">{{ colLabel }} </th>
{%- endfor -%}
<th style="text-align: right;">Total</th>
</tr>
<tfoot>
<tr>
<td>Total
{% for total in profile.total %}
<td style="text-align: right">{{ total|number_format }}
{% endfor %}
<tbody>
{% for rowLabel in rowLabels -%}
<tr>
<td>{{ rowLabel }}
{%- for colLabel in colLabels -%}
<td style="text-align: right">
{%- if profile[rowLabel][colLabel] is defined -%}
{{ profile[rowLabel][colLabel]|number_format }}{%- else -%}
0{%- endif -%}
{% endfor %}
<td style="text-align: right">{{ profile[rowLabel]['total']|number_format }}
</tr>
{%- endfor -%}
</table>
交叉表服务:
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 as $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 as $key => $array) {
if (!array_key_exists('total', $array)) {
$profile[$key]['total'] = 0;
}
}
$profile['total'] = [];
foreach ($profile as $row => $array) {
foreach ($array as $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 as $col) {
$colKeys[$col] = 0;
}
$profile = [];
foreach ($rows as $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' AND '$endText' ";
}
}
县资料库:
class CountyRepository extends EntityRepository
{
public function colLabels($dateCriteria)
{
$str = "select distinct cty.county from county cty
join contact c on c.county_id = cty.id
where c.contact_date BETWEEN __DATE_CRITERIA__
order by county";
$sql = str_replace('__DATE_CRITERIA__', $dateCriteria, $str);
$conn = $this->getEntityManager()->getConnection();
$stmt = $conn->executeQuery($sql);
$colArray = $stmt->fetchAll();
$colLabels = [];
foreach ($colArray as $array) {
$colLabels[] = $array['county'];
}
return $colLabels;
}
收入库:
class IncomeRepository extends EntityRepository
{
public function rowLabels($dateCriteria)
{
$str = "select distinct i.income
from income i
join household h on h.income_id = i.id
join contact c on c.household_id = h.id
WHERE c.contact_date BETWEEN __DATE_CRITERIA__
AND i.enabled = TRUE order by i.id ";
$sql = str_replace('__DATE_CRITERIA__', $dateCriteria, $str);
$conn = $this->getEntityManager()->getConnection();
$stmt = $conn->executeQuery($sql);
$rowArray = $stmt->fetchAll();
$rowLabels = [];
foreach ($rowArray as $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 i ON h.income_id = i.id " .
"WHERE c.contact_date BETWEEN __DATE_CRITERIA__ " .
"AND i.enabled = TRUE " .
"GROUP BY 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();
}
你的例子对我帮助很大。谢谢你。
所以,我使用了 :
$conn = $this->getEntityManager()->getConnection();
$stmt = $conn->executeQuery($sql);
$rowArray = $stmt->fetchAll();
在我的存储库中使用此查询获取我的数据,return 结果如我所愿:
$sql = "select a.id, a.name, v.date,
GROUP_CONCAT(if(id_kpi = 400, value, NULL)) AS 'Coverage',
GROUP_CONCAT(if(id_kpi = 401, value, NULL)) AS 'Automation',
GROUP_CONCAT(if(id_kpi = 402, value, NULL)) AS 'NEW TC',
GROUP_CONCAT(if(id_kpi = 403, value, NULL)) AS 'TOTAL TC'
from applications as a
left join piqua_kpi.values as v on a.id = v.id_application
".$filter.$date."
group by a.name, v.date";
然后在我看来,我用 twig 遍历我的数组来打印我的值。
所以现在,我将尝试添加一个内联可编辑网格来编辑这些数字并添加新值。我希望这种获取数据的方式不会让事情变得更难。
我正在使用 Symfony、Doctrine 和 jQuery 开发用于应用程序质量服务的 KPI 管理工具。
我创建了三个实体,它们是:
- 申请(ID、姓名...)
- KPI(ID、名称、类别...)
- 值(Application_ID、KPI_ID、月、值)
所以我的值 table 有一些值,例如:
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...
但我需要这样打印:
Application| Month | KPI 1 | KPI 2 | KPI 3 | KPI 4 | .......
10 |January-15 | 40 | 50 | true | commentaire | .....
22 |January-15 | 40 | 40 | false | commentaire | .....
我没有做到。不知道我的构思和造型好不好
我设法通过此查询获得了我想要的格式(在 MySQL 中):
$sql = "select a.id, a.name, v.date,
GROUP_CONCAT(if(id_kpi = 400, value, NULL)) AS 'Coverage',
GROUP_CONCAT(if(id_kpi = 401, value, NULL)) AS 'Automation',
GROUP_CONCAT(if(id_kpi = 402, value, NULL)) AS 'NEW TC',
GROUP_CONCAT(if(id_kpi = 403, value, NULL)) AS 'TOTAL TC',
GROUP_CONCAT(if(id_kpi = 409, value, NULL)) AS 'Release/Month'
from applications as a
left join piqua_kpi.values as v on a.id = v.id_application
group by a.name, v.date";
但我没能对 Doctrine 做同样的事情并得到一个我可以使用的对象。
这是我用来为客户创建一系列交叉表报告的方法。这样做需要至少抽象一些数据透视表 table 的格式。希望对你有帮助。
首先是一个报表示例:
在此报告中,县实体(链接到家庭地址)是列,家庭实体收入等级是行条目。单元格是在给定时间段内联系的具有行和列特征的家庭的计数。
控制器(一共三块):
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 the 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,
]);
}
模板:
{% 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 }}
{%- for colLabel in colLabels -%}
<th style="text-align: right;">{{ colLabel }} </th>
{%- endfor -%}
<th style="text-align: right;">Total</th>
</tr>
<tfoot>
<tr>
<td>Total
{% for total in profile.total %}
<td style="text-align: right">{{ total|number_format }}
{% endfor %}
<tbody>
{% for rowLabel in rowLabels -%}
<tr>
<td>{{ rowLabel }}
{%- for colLabel in colLabels -%}
<td style="text-align: right">
{%- if profile[rowLabel][colLabel] is defined -%}
{{ profile[rowLabel][colLabel]|number_format }}{%- else -%}
0{%- endif -%}
{% endfor %}
<td style="text-align: right">{{ profile[rowLabel]['total']|number_format }}
</tr>
{%- endfor -%}
</table>
交叉表服务:
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 as $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 as $key => $array) {
if (!array_key_exists('total', $array)) {
$profile[$key]['total'] = 0;
}
}
$profile['total'] = [];
foreach ($profile as $row => $array) {
foreach ($array as $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 as $col) {
$colKeys[$col] = 0;
}
$profile = [];
foreach ($rows as $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' AND '$endText' ";
}
}
县资料库:
class CountyRepository extends EntityRepository
{
public function colLabels($dateCriteria)
{
$str = "select distinct cty.county from county cty
join contact c on c.county_id = cty.id
where c.contact_date BETWEEN __DATE_CRITERIA__
order by county";
$sql = str_replace('__DATE_CRITERIA__', $dateCriteria, $str);
$conn = $this->getEntityManager()->getConnection();
$stmt = $conn->executeQuery($sql);
$colArray = $stmt->fetchAll();
$colLabels = [];
foreach ($colArray as $array) {
$colLabels[] = $array['county'];
}
return $colLabels;
}
收入库:
class IncomeRepository extends EntityRepository
{
public function rowLabels($dateCriteria)
{
$str = "select distinct i.income
from income i
join household h on h.income_id = i.id
join contact c on c.household_id = h.id
WHERE c.contact_date BETWEEN __DATE_CRITERIA__
AND i.enabled = TRUE order by i.id ";
$sql = str_replace('__DATE_CRITERIA__', $dateCriteria, $str);
$conn = $this->getEntityManager()->getConnection();
$stmt = $conn->executeQuery($sql);
$rowArray = $stmt->fetchAll();
$rowLabels = [];
foreach ($rowArray as $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 i ON h.income_id = i.id " .
"WHERE c.contact_date BETWEEN __DATE_CRITERIA__ " .
"AND i.enabled = TRUE " .
"GROUP BY 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();
}
你的例子对我帮助很大。谢谢你。 所以,我使用了 :
$conn = $this->getEntityManager()->getConnection();
$stmt = $conn->executeQuery($sql);
$rowArray = $stmt->fetchAll();
在我的存储库中使用此查询获取我的数据,return 结果如我所愿:
$sql = "select a.id, a.name, v.date,
GROUP_CONCAT(if(id_kpi = 400, value, NULL)) AS 'Coverage',
GROUP_CONCAT(if(id_kpi = 401, value, NULL)) AS 'Automation',
GROUP_CONCAT(if(id_kpi = 402, value, NULL)) AS 'NEW TC',
GROUP_CONCAT(if(id_kpi = 403, value, NULL)) AS 'TOTAL TC'
from applications as a
left join piqua_kpi.values as v on a.id = v.id_application
".$filter.$date."
group by a.name, v.date";
然后在我看来,我用 twig 遍历我的数组来打印我的值。
所以现在,我将尝试添加一个内联可编辑网格来编辑这些数字并添加新值。我希望这种获取数据的方式不会让事情变得更难。