Я разрабатываю инструмент управления KPI для служб качества приложений с помощью Symfony, Doctrine и jQuery.
Я создал три объекта:
поэтому моя таблица значений имеет некоторые значения, такие как:
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 и получить объект, который я могу использовать.
Вот подход, который я использовал для создания серии перекрестных отчетов для клиента. Для этого необходимо абстрагировать хотя бы часть форматирования сводной таблицы. Надеюсь, поможет.
Сначала пример отчета:
В этом отчете округ (связанный с адресом домохозяйства) — это столбец, а уровень дохода субъекта домохозяйства — запись в строке. Ячейки — это количество домохозяйств, с которыми связались в данный период времени с характеристиками строки и столбца.
Контроллер (есть три штуки):
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();
в моем хранилище, чтобы получить мои данные с этим запросом, которые возвращают результат, как я хочу:
$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";
и затем, на мой взгляд, я зацикливаю веточку по массиву, чтобы напечатать мои значения.
Итак, теперь я попытаюсь добавить встроенную редактируемую сетку для редактирования этих фигур и добавления новых значений. Надеюсь, этот способ получения моих данных не усложнит ситуацию.