src/CompanyGroupBundle/Controller/TicketController.php line 117

Open in your IDE?
  1. <?php
  2. namespace CompanyGroupBundle\Controller;
  3. use ApplicationBundle\Controller\GenericController;
  4. use CompanyGroupBundle\Entity\EntityTicket;
  5. use CompanyGroupBundle\Entity\EntityTicketReport;
  6. use Symfony\Component\HttpFoundation\JsonResponse;
  7. use Symfony\Component\HttpFoundation\Request;
  8. /**
  9.  * TicketController
  10.  *
  11.  * Handles the full ticket lifecycle:
  12.  *   - Manual ticket list / create / edit / view
  13.  *   - Status transitions: resolve / close / assign
  14.  *   - System reporter endpoint with 1-hour problemHash deduplication
  15.  *
  16.  * Entity manager: company_group
  17.  * Views: CompanyGroupBundle:pages/tickets:*.html.twig
  18.  */
  19. class TicketController extends GenericController
  20. {
  21.     // =========================================================================
  22.     // CONSTANTS
  23.     // =========================================================================
  24.     const STATUS_OPEN        1;
  25.     const STATUS_IN_PROGRESS 2;
  26.     const STATUS_RESOLVED    3;
  27.     const STATUS_CLOSED      4;
  28.     const SOURCE_MANUAL 1;
  29.     const SOURCE_SYSTEM 2;
  30.     const SOURCE_API    3;
  31.     const DEDUP_WINDOW_SECONDS 3600// 1 hour
  32.     const TICKETS_PER_PAGE 20;
  33.     // =========================================================================
  34.     // LIST
  35.     // =========================================================================
  36.     /**
  37.      * GET /tickets
  38.      * Paginated, filterable list of all tickets.
  39.      */
  40.     public function TicketListAction(Request $request)
  41.     {
  42.         $em $this->getDoctrine()->getManager('company_group');
  43.         // ── Filters ──────────────────────────────────────────────────────────
  44.         $filters = [
  45.             'status'   => $request->query->get('status',   null),
  46.             'priority' => $request->query->get('priority'null),
  47.             'source'   => $request->query->get('source',   null),
  48.             'q'        => trim($request->query->get('q',   '')),
  49.         ];
  50.         $page        max(1, (int) $request->query->get('page'1));
  51.         $offset      = ($page 1) * self::TICKETS_PER_PAGE;
  52.         // ── Build DQL ────────────────────────────────────────────────────────
  53.         $qb $em->createQueryBuilder()
  54.             ->select('t')
  55.             ->from('CompanyGroupBundle\Entity\EntityTicket''t')
  56.             ->orderBy('t.createdAt''DESC');
  57.         if ($filters['status'] !== null && $filters['status'] !== '') {
  58.             $qb->andWhere('t.status = :status')->setParameter('status', (int) $filters['status']);
  59.         }
  60.         if ($filters['priority'] !== null && $filters['priority'] !== '') {
  61.             $qb->andWhere('t.urgency = :priority')->setParameter('priority', (int) $filters['priority']);
  62.         }
  63.         if ($filters['source'] !== null && $filters['source'] !== '') {
  64.             $qb->andWhere('t.source = :source')->setParameter('source', (int) $filters['source']);
  65.         }
  66.         if ($filters['q'] !== '') {
  67.             $qb->andWhere(
  68.                 $qb->expr()->orX(
  69.                     $qb->expr()->like('t.title',        ':q'),
  70.                     $qb->expr()->like('t.ticketNumber'':q'),
  71.                     $qb->expr()->like('t.ticketBody',   ':q')
  72.                 )
  73.             )->setParameter('q''%' $filters['q'] . '%');
  74.         }
  75.         // ── Count for pagination ──────────────────────────────────────────────
  76.         $countQb = clone $qb;
  77.         $total   = (int) $countQb->select('COUNT(t.id)')->getQuery()->getSingleScalarResult();
  78.         $tickets    $qb->setFirstResult($offset)->setMaxResults(self::TICKETS_PER_PAGE)->getQuery()->getResult();
  79.         $totalPages = (int) ceil($total self::TICKETS_PER_PAGE);
  80.         // ── Summary counts ────────────────────────────────────────────────────
  81.         $summary $this->_getStatusSummary($em);
  82.         return $this->render('@CompanyGroup/pages/tickets/list_tickets.html.twig', [
  83.             'tickets'     => $tickets,
  84.             'filters'     => $filters,
  85.             'currentPage' => $page,
  86.             'totalPages'  => $totalPages,
  87.             'total'       => $total,
  88.             'summary'     => $summary,
  89.         ]);
  90.     }
  91.     // =========================================================================
  92.     // VIEW
  93.     // =========================================================================
  94.     /**
  95.      * GET /ticket/{id}
  96.      */
  97.     public function ViewTicketAction(Request $request$id)
  98.     {
  99.         $em $this->getDoctrine()->getManager('company_group');
  100.         $ticket $em->getRepository('CompanyGroupBundle\Entity\EntityTicket')->find($id);
  101.         if (!$ticket) {
  102.             throw $this->createNotFoundException('Ticket #' $id ' not found.');
  103.         }
  104.         // Load occurrence log (EntityTicketReport rows for this ticket)
  105.         $reports $em->createQueryBuilder()
  106.             ->select('r')
  107.             ->from('CompanyGroupBundle\Entity\EntityTicketReport''r')
  108.             ->where('r.ticketId = :tid')
  109.             ->setParameter('tid'$id)
  110.             ->orderBy('r.reportedAt''DESC')
  111.             ->setMaxResults(100)
  112.             ->getQuery()
  113.             ->getResult();
  114.         return $this->render('@CompanyGroup/pages/tickets/view_ticket.html.twig', [
  115.             'ticket'  => $ticket,
  116.             'reports' => $reports,
  117.         ]);
  118.     }
  119.     // =========================================================================
  120.     // CREATE
  121.     // =========================================================================
  122.     /**
  123.      * GET /ticket/create  — show blank form
  124.      */
  125.     public function CreateTicketAction(Request $request)
  126.     {
  127.         return $this->render('@CompanyGroup/pages/tickets/create_ticket.html.twig', [
  128.             'formData' => $this->_emptyFormData(),
  129.         ]);
  130.     }
  131.     /**
  132.      * POST /ticket/create/submit  — persist new ticket
  133.      */
  134.     public function CreateTicketSubmitAction(Request $request)
  135.     {
  136.         $session $request->getSession();
  137.         $em      $this->getDoctrine()->getManager('company_group');
  138.         $post    $request->request;
  139.         $ticket = new EntityTicket();
  140.         $ticket->setTitle(trim($post->get('title''')));
  141.         $ticket->setTicketBody(trim($post->get('description''')));
  142.         $ticket->setNote(trim($post->get('note''')));
  143.         $ticket->setType((int) $post->get('ticketType'0) ?: null);
  144.         $ticket->setStatus(self::STATUS_OPEN);
  145.         $ticket->setUrgency((int) $post->get('priority'3));
  146.         $ticket->setSource((int) $post->get('source'self::SOURCE_MANUAL));
  147.         $ticket->setSystemCreated(false);
  148.         $ticket->setUserId($this->loggedUserId($request));
  149.         // Ownership
  150.         if ($post->get('assignedToUserId')) {
  151.             $ticket->setAssignedToUserId((int) $post->get('assignedToUserId'));
  152.             $ticket->setAssignedByUserId($this->loggedUserId($request));
  153.             $ticket->setTicketAssignedDate(new \DateTime());
  154.         }
  155.         if ($post->get('taggedUserIds')) {
  156.             $ticket->setTaggedUserIds($post->get('taggedUserIds'));
  157.         }
  158.         if ($post->get('companyId')) {
  159.             $ticket->setCompanyId((int) $post->get('companyId'));
  160.         }
  161.         if ($post->get('appId')) {
  162.             $ticket->setAppId((int) $post->get('appId'));
  163.         }
  164.         if ($post->get('branchId')) {
  165.             $ticket->setBranchId((int) $post->get('branchId'));
  166.         }
  167.         // Reporter contact
  168.         $ticket->setName(trim($post->get('name''')));
  169.         $ticket->setEmail(trim($post->get('email''')));
  170.         $ticket->setPhone(trim($post->get('phone''')));
  171.         if ($post->get('preferredContactMethod')) {
  172.             $ticket->setPreferredContactMethod((int) $post->get('preferredContactMethod'));
  173.         }
  174.         // Deadline
  175.         if ($post->get('deadlineDate')) {
  176.             try {
  177.                 $ticket->setDeadlineDate(new \DateTime($post->get('deadlineDate')));
  178.             } catch (\Exception $e) {
  179.                 // ignore bad date format
  180.             }
  181.         }
  182.         // Feedback
  183.         if ($post->get('feedback')) {
  184.             $ticket->setFeedback(trim($post->get('feedback')));
  185.         }
  186.         // System tracking fields (advanced section)
  187.         if ($post->get('problemHash')) {
  188.             $ticket->setProblemHash(trim($post->get('problemHash')));
  189.         }
  190.         if ($post->get('serverIdentifier')) {
  191.             $ticket->setServerIdentifier(trim($post->get('serverIdentifier')));
  192.         }
  193.         if ($post->get('lastPayloadJson')) {
  194.             $ticket->setLastPayloadJson(trim($post->get('lastPayloadJson')));
  195.         }
  196.         $em->persist($ticket);
  197.         $em->flush();
  198.         $this->addFlash('success''Ticket ' $ticket->getTicketNumber() . ' created successfully.');
  199.         return $this->redirectToRoute('ticket_view', ['id' => $ticket->getId()]);
  200.     }
  201.     // =========================================================================
  202.     // EDIT
  203.     // =========================================================================
  204.     /**
  205.      * GET /ticket/edit/{id}  — show pre-filled form
  206.      */
  207.     public function EditTicketAction(Request $request$id)
  208.     {
  209.         $em $this->getDoctrine()->getManager('company_group');
  210.         $ticket $em->getRepository('CompanyGroupBundle\Entity\EntityTicket')->find($id);
  211.         if (!$ticket) {
  212.             throw $this->createNotFoundException('Ticket #' $id ' not found.');
  213.         }
  214.         // Map entity back to form data array for the view
  215.         $formData = [
  216.             'title'                  => $ticket->getTitle(),
  217.             'description'            => $ticket->getTicketBody(),
  218.             'note'                   => $ticket->getNote(),
  219.             'ticketType'             => $ticket->getType(),
  220.             'priority'               => $ticket->getUrgency(),
  221.             'source'                 => $ticket->getSource(),
  222.             'assignedToUserId'       => $ticket->getAssignedToUserId(),
  223.             'taggedUserIds'          => $ticket->getTaggedUserIds(),
  224.             'companyId'              => $ticket->getCompanyId(),
  225.             'appId'                  => $ticket->getAppId(),
  226.             'branchId'               => $ticket->getBranchId(),
  227.             'name'                   => $ticket->getName(),
  228.             'email'                  => $ticket->getEmail(),
  229.             'phone'                  => $ticket->getPhone(),
  230.             'preferredContactMethod' => $ticket->getPreferredContactMethod(),
  231.             'deadlineDate'           => $ticket->getDeadlineDate() ? $ticket->getDeadlineDate()->format('Y-m-d') : '',
  232.             'problemHash'            => $ticket->getProblemHash(),
  233.             'serverIdentifier'       => $ticket->getServerIdentifier(),
  234.             'lastPayloadJson'        => $ticket->getLastPayloadJson(),
  235.             'feedback'               => $ticket->getFeedback(),
  236.         ];
  237.         return $this->render('@CompanyGroup/pages/tickets/create_ticket.html.twig', [
  238.             'ticket'   => $ticket,
  239.             'formData' => $formData,
  240.             'editMode' => true,
  241.         ]);
  242.     }
  243.     /**
  244.      * POST /ticket/edit/{id}/submit
  245.      */
  246.     public function EditTicketSubmitAction(Request $request$id)
  247.     {
  248.         $em     $this->getDoctrine()->getManager('company_group');
  249.         $post   $request->request;
  250.         $ticket $em->getRepository('CompanyGroupBundle\Entity\EntityTicket')->find($id);
  251.         if (!$ticket) {
  252.             throw $this->createNotFoundException('Ticket #' $id ' not found.');
  253.         }
  254.         $ticket->setTitle(trim($post->get('title''')));
  255.         $ticket->setTicketBody(trim($post->get('description''')));
  256.         $ticket->setNote(trim($post->get('note''')));
  257.         $ticket->setType((int) $post->get('ticketType'0) ?: null);
  258.         $ticket->setUrgency((int) $post->get('priority'3));
  259.         $ticket->setSource((int) $post->get('source'self::SOURCE_MANUAL));
  260.         $ticket->setName(trim($post->get('name''')));
  261.         $ticket->setEmail(trim($post->get('email''')));
  262.         $ticket->setPhone(trim($post->get('phone''')));
  263.         if ($post->get('preferredContactMethod')) {
  264.             $ticket->setPreferredContactMethod((int) $post->get('preferredContactMethod'));
  265.         }
  266.         if ($post->get('assignedToUserId')) {
  267.             if (!$ticket->getAssignedToUserId()) {
  268.                 $ticket->setTicketAssignedDate(new \DateTime());
  269.                 $ticket->setAssignedByUserId($this->loggedUserId($request));
  270.             }
  271.             $ticket->setAssignedToUserId((int) $post->get('assignedToUserId'));
  272.         }
  273.         if ($post->get('taggedUserIds')) {
  274.             $ticket->setTaggedUserIds($post->get('taggedUserIds'));
  275.         }
  276.         if ($post->get('companyId')) {
  277.             $ticket->setCompanyId((int) $post->get('companyId'));
  278.         }
  279.         if ($post->get('appId')) {
  280.             $ticket->setAppId((int) $post->get('appId'));
  281.         }
  282.         if ($post->get('branchId')) {
  283.             $ticket->setBranchId((int) $post->get('branchId'));
  284.         }
  285.         if ($post->get('deadlineDate')) {
  286.             try {
  287.                 $ticket->setDeadlineDate(new \DateTime($post->get('deadlineDate')));
  288.             } catch (\Exception $e) {}
  289.         }
  290.         if ($post->get('problemHash')) {
  291.             $ticket->setProblemHash(trim($post->get('problemHash')));
  292.         }
  293.         if ($post->get('serverIdentifier')) {
  294.             $ticket->setServerIdentifier(trim($post->get('serverIdentifier')));
  295.         }
  296.         if ($post->get('lastPayloadJson')) {
  297.             $ticket->setLastPayloadJson(trim($post->get('lastPayloadJson')));
  298.         }
  299.         if ($post->get('feedback')) {
  300.             $ticket->setFeedback(trim($post->get('feedback')));
  301.         }
  302.         $em->flush();
  303.         $this->addFlash('success''Ticket ' $ticket->getTicketNumber() . ' updated.');
  304.         return $this->redirectToRoute('ticket_view', ['id' => $ticket->getId()]);
  305.     }
  306.     // =========================================================================
  307.     // STATUS TRANSITIONS
  308.     // =========================================================================
  309.     /**
  310.      * GET /ticket/resolve/{id}
  311.      */
  312.     public function ResolveTicketAction(Request $request$id)
  313.     {
  314.         $em     $this->getDoctrine()->getManager('company_group');
  315.         $ticket $em->getRepository('CompanyGroupBundle\Entity\EntityTicket')->find($id);
  316.         if (!$ticket) {
  317.             throw $this->createNotFoundException('Ticket #' $id ' not found.');
  318.         }
  319.         $ticket->setStatus(self::STATUS_RESOLVED);
  320.         $ticket->setResolvedAt(new \DateTime());
  321.         $em->flush();
  322.         $this->addFlash('success''Ticket ' $ticket->getTicketNumber() . ' marked as resolved.');
  323.         return $this->redirectToRoute('ticket_view', ['id' => $id]);
  324.     }
  325.     /**
  326.      * GET /ticket/close/{id}
  327.      */
  328.     public function CloseTicketAction(Request $request$id)
  329.     {
  330.         $em     $this->getDoctrine()->getManager('company_group');
  331.         $ticket $em->getRepository('CompanyGroupBundle\Entity\EntityTicket')->find($id);
  332.         if (!$ticket) {
  333.             throw $this->createNotFoundException('Ticket #' $id ' not found.');
  334.         }
  335.         $ticket->setStatus(self::STATUS_CLOSED);
  336.         $ticket->setClosedAt(new \DateTime());
  337.         $em->flush();
  338.         $this->addFlash('success''Ticket ' $ticket->getTicketNumber() . ' closed.');
  339.         return $this->redirectToRoute('ticket_view', ['id' => $id]);
  340.     }
  341.     /**
  342.      * POST /ticket/assign/{id}
  343.      * Body params: assignedToUserId
  344.      */
  345.     public function AssignTicketAction(Request $request$id)
  346.     {
  347.         $em      $this->getDoctrine()->getManager('company_group');
  348.         $ticket  $em->getRepository('CompanyGroupBundle\Entity\EntityTicket')->find($id);
  349.         if (!$ticket) {
  350.             return new JsonResponse(['success' => false'message' => 'Ticket not found'], 404);
  351.         }
  352.         $assignTo = (int) $request->request->get('assignedToUserId'0);
  353.         if (!$assignTo) {
  354.             return new JsonResponse(['success' => false'message' => 'assignedToUserId required'], 400);
  355.         }
  356.         $ticket->setAssignedToUserId($assignTo);
  357.         $ticket->setAssignedByUserId($this->loggedUserId($request));
  358.         $ticket->setTicketAssignedDate(new \DateTime());
  359.         if ($ticket->getStatus() === self::STATUS_OPEN) {
  360.             $ticket->setStatus(self::STATUS_IN_PROGRESS);
  361.         }
  362.         $em->flush();
  363.         return new JsonResponse([
  364.             'success'      => true,
  365.             'ticketNumber' => $ticket->getTicketNumber(),
  366.             'assignedTo'   => $assignTo,
  367.         ]);
  368.     }
  369.     // =========================================================================
  370.     // SYSTEM REPORTER  (called by cron / error handlers, no browser session)
  371.     // =========================================================================
  372.     /**
  373.      * POST /ticket/system/report
  374.      *
  375.      * Expected JSON body:
  376.      * {
  377.      *   "errorCode":       "DB_CONN_FAIL",
  378.      *   "message":         "Could not connect to database",
  379.      *   "exceptionClass":  "Doctrine\\DBAL\\Exception",
  380.      *   "fileLocation":    "src/Bundle/Service/ReportService.php:142",
  381.      *   "serverIdentifier":"app-server-01",
  382.      *   "appId":           3,
  383.      *   "payloadJson":     "{...full raw error payload...}"
  384.      * }
  385.      *
  386.      * Returns JSON: { success, ticketId, ticketNumber, action: "created"|"merged" }
  387.      */
  388.     public function SystemReportAction(Request $request)
  389.     {
  390.         // ── Parse input ───────────────────────────────────────────────────────
  391.         $body json_decode($request->getContent(), true);
  392.         if (!$body) {
  393.             $body $request->request->all();
  394.         }
  395.         $errorCode       $body['errorCode']       ?? '';
  396.         $message         $body['message']          ?? '';
  397.         $exceptionClass  $body['exceptionClass']   ?? '';
  398.         $fileLocation    $body['fileLocation']     ?? '';
  399.         $serverIdentifier$body['serverIdentifier'] ?? gethostname();
  400.         $appId           = isset($body['appId']) ? (int) $body['appId'] : null;
  401.         $payloadJson     = isset($body['payloadJson'])
  402.             ? (is_string($body['payloadJson']) ? $body['payloadJson'] : json_encode($body['payloadJson']))
  403.             : json_encode($body);
  404.         // ── Compute problemHash (stable attributes only, NO timestamps/userIds) ──
  405.         $hashInput   implode('|', [$errorCode$exceptionClass$fileLocation, (string) $appId]);
  406.         $problemHash hash('sha256'$hashInput);
  407.         $em  $this->getDoctrine()->getManager('company_group');
  408.         $now = new \DateTime();
  409.         // ── Deduplication: look for open ticket with same hash within 1 hour ──
  410.         $windowStart = (clone $now)->modify('-' self::DEDUP_WINDOW_SECONDS ' seconds');
  411.         $existing $em->createQueryBuilder()
  412.             ->select('t')
  413.             ->from('CompanyGroupBundle\Entity\EntityTicket''t')
  414.             ->where('t.problemHash = :hash')
  415.             ->andWhere('t.lastReportedAt >= :window')
  416.             ->andWhere('t.status != :closed')
  417.             ->setParameter('hash',   $problemHash)
  418.             ->setParameter('window'$windowStart)
  419.             ->setParameter('closed'self::STATUS_CLOSED)
  420.             ->setMaxResults(1)
  421.             ->getQuery()
  422.             ->getOneOrNullResult();
  423.         if ($existing) {
  424.             // ── MERGE: increment counter, refresh last payload ─────────────
  425.             $existing->setReportCount($existing->getReportCount() + 1);
  426.             $existing->setLastReportedAt($now);
  427.             $existing->setLastPayloadJson($payloadJson);
  428.             if ($serverIdentifier) {
  429.                 $existing->setServerIdentifier($serverIdentifier);
  430.             }
  431.             $ticket $existing;
  432.             $action 'merged';
  433.         } else {
  434.             // ── CREATE: new ticket (new incident window) ───────────────────
  435.             $ticket = new EntityTicket();
  436.             $ticket->setTitle('[' $errorCode '] ' mb_substr($message0200));
  437.             $ticket->setTicketBody($message);
  438.             $ticket->setStatus(self::STATUS_OPEN);
  439.             $ticket->setUrgency(1); // emergency by default for system errors
  440.             $ticket->setSource(self::SOURCE_SYSTEM);
  441.             $ticket->setSystemCreated(true);
  442.             $ticket->setProblemHash($problemHash);
  443.             $ticket->setReportCount(1);
  444.             $ticket->setFirstReportedAt($now);
  445.             $ticket->setLastReportedAt($now);
  446.             $ticket->setLastPayloadJson($payloadJson);
  447.             $ticket->setServerIdentifier($serverIdentifier);
  448.             if ($appId) {
  449.                 $ticket->setAppId($appId);
  450.             }
  451.             $em->persist($ticket);
  452.             $action 'created';
  453.         }
  454.         // ── Always log one EntityTicketReport row per occurrence ──────────────
  455.         $em->flush(); // flush ticket first so we have its ID if just created
  456.         $report = new EntityTicketReport();
  457.         $report->setTicketId($ticket->getId());
  458.         $report->setMessage($message);
  459.         $report->setErrorCode($errorCode);
  460.         $report->setServerIdentifier($serverIdentifier);
  461.         $report->setPayloadJson($payloadJson);
  462.         $report->setReportedAt($now);
  463.         $em->persist($report);
  464.         $em->flush();
  465.         return new JsonResponse([
  466.             'success'      => true,
  467.             'action'       => $action,
  468.             'ticketId'     => $ticket->getId(),
  469.             'ticketNumber' => $ticket->getTicketNumber(),
  470.             'reportCount'  => $ticket->getReportCount(),
  471.         ]);
  472.     }
  473.     // =========================================================================
  474.     // PRIVATE HELPERS
  475.     // =========================================================================
  476.     /**
  477.      * Returns a fully-keyed formData array with safe defaults.
  478.      * Prevents Twig strict-access errors when the form is blank (create mode).
  479.      */
  480.     private function _emptyFormData()
  481.     {
  482.         return [
  483.             'title'                  => '',
  484.             'description'            => '',
  485.             'note'                   => '',
  486.             'ticketType'             => null,
  487.             'priority'               => 3,
  488.             'source'                 => 1,
  489.             'assignedToUserId'       => null,
  490.             'taggedUserIds'          => '',
  491.             'companyId'              => null,
  492.             'appId'                  => null,
  493.             'branchId'               => null,
  494.             'name'                   => '',
  495.             'email'                  => '',
  496.             'phone'                  => '',
  497.             'preferredContactMethod' => null,
  498.             'deadlineDate'           => '',
  499.             'problemHash'            => '',
  500.             'serverIdentifier'       => '',
  501.             'lastPayloadJson'        => '',
  502.             'errorCode'              => '',
  503.             'feedback'               => '',
  504.         ];
  505.     }
  506.     /**
  507.      * Returns ticket counts grouped by status.
  508.      */
  509.     private function _getStatusSummary($em)
  510.     {
  511.         $rows $em->createQueryBuilder()
  512.             ->select('t.status, COUNT(t.id) as cnt')
  513.             ->from('CompanyGroupBundle\Entity\EntityTicket''t')
  514.             ->groupBy('t.status')
  515.             ->getQuery()
  516.             ->getArrayResult();
  517.         $map = ['open' => 0'inProgress' => 0'resolved' => 0'closed' => 0];
  518.         foreach ($rows as $row) {
  519.             switch ((int) $row['status']) {
  520.                 case self::STATUS_OPEN:        $map['open']       = (int) $row['cnt']; break;
  521.                 case self::STATUS_IN_PROGRESS$map['inProgress'] = (int) $row['cnt']; break;
  522.                 case self::STATUS_RESOLVED:    $map['resolved']   = (int) $row['cnt']; break;
  523.                 case self::STATUS_CLOSED:      $map['closed']     = (int) $row['cnt']; break;
  524.             }
  525.         }
  526.         return $map;
  527.     }
  528. }