一种使用 Symfony2 和 Doctrine 的枢轴 table

A kind of pivot table using Symfony2 and Doctrine

我正在使用 Symfony、Doctrine 和 jQuery 开发用于应用程序质量服务的 KPI 管理工具。

我创建了三个实体,它们是:

所以我的值 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 遍历我的数组来打印我的值。

所以现在,我将尝试添加一个内联可编辑网格来编辑这些数字并添加新值。我希望这种获取数据的方式不会让事情变得更难。