dominium/ticket-bundle

This bundle provides a customer support system by integrating a ticketing functionality for Symfony2 applications.

Installs: 16

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 1

Forks: 2

Type:symfony-bundle

v0.9.2 2016-06-20 22:15 UTC

This package is not auto-updated.

Last update: 2024-04-19 17:00:50 UTC


README

This bundle allows you to integrate in your Symfony2 project a basic system of customer support by tickets. You can create, edit and respond tickets instantly after installing this bundle .

Needs a minimum configuration and the whole procedure is very simple.

Installation.

I. Installing the bundle in two different ways.

a) Automatically.

You can install the last version of this bundle with composer running the command from your command line:

$ composer require kdrmklabs/ticket-bundle

b) By composer.json .

Or you can install this bundle by adding next code line to your project in the composer.json file and after update it with the command composer update

file: /composer.json

{
    "require": {
        "kdrmklabs/ticket-bundle": "dev-master",
    }
}

Now, update the bundle with composer:

$ composer update kdrmklabs/ticket-bundle

II. Enable and register the Bundle in the AppKernel

// file: app/AppKernel.php

public function registerBundles()
{
    $bundles = array(
        // ...
        new Kdrmklabs\Bundle\TicketBundle\KdrmklabsTicketBundle(),
        // ...
        // Your application bundles
    );
}

III. Install Gedmon Doctrine2 extensions

Requirements:

Check if gedmo doctrine extensions are downloaded.

/vendor/gedmo/doctrine-extensions/lib/Gedmo

if not download it:

a. Add dependency to composer.json

file: /composer.json

{
    "require": {
        ....
        "gedmo/doctrine-extensions": "dev-master"
    }
}

b. Update with composer in the command line:

$ php composer.phar update gedmo/doctrine-extensions

Configure Gedmo:

III.I. Create a DoctrineExtensionListener

// file: src/AppBundle/Listener/DoctrineExtensionListener.php

namespace AppBundle\Listener;

use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class DoctrineExtensionListener implements ContainerAwareInterface
{
    /**
     * @var ContainerInterface
     */
    protected $container;

    public function setContainer(ContainerInterface $container = null)
    {
        $this->container = $container;
    }

    public function onLateKernelRequest(GetResponseEvent $event)
    {
        $translatable = $this->container->get('gedmo.listener.translatable');
        $translatable->setTranslatableLocale($event->getRequest()->getLocale());
    }

    public function onKernelRequest(GetResponseEvent $event)
    {
        $securityContext = $this->container->get('security.context', ContainerInterface::NULL_ON_INVALID_REFERENCE);
        if (null !== $securityContext && null !== $securityContext->getToken() && $securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
            $loggable = $this->container->get('gedmo.listener.loggable');
            $loggable->setUsername($securityContext->getToken()->getUsername());
        }
    }
}

III.II. Create file doctrine_extensions.yml and locate it in your app/config directory

# file: app/config/doctrine_extensions.yml

doctrine:
    orm:
        auto_generate_proxy_classes: %kernel.debug%
        auto_mapping: true
# only these lines are added additionally
        mappings:
            translatable:
                type: annotation
                alias: Gedmo
                prefix: Gedmo\Translatable\Entity
                # make sure vendor library location is correct
                dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity"
    # services to handle doctrine extensions
    # import it in config.yml
services:
    # KernelRequest listener
    extension.listener:
        # change this to your DoctrineExtensionListener namespace
        class: AppBundle\Listener\DoctrineExtensionListener
        calls:
            - [ setContainer, [ @service_container ] ]
        tags:
            # translatable sets locale after router processing
            - { name: kernel.event_listener, event: kernel.request, method: onLateKernelRequest, priority: -10 }
            # loggable hooks user username if one is in security context
            - { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }

    # Doctrine Extension listeners to handle behaviors
    gedmo.listener.tree:
        class: Gedmo\Tree\TreeListener
        tags:
            - { name: doctrine.event_subscriber, connection: default }
        calls:
            - [ setAnnotationReader, [ @annotation_reader ] ]

    gedmo.listener.translatable:
        class: Gedmo\Translatable\TranslatableListener
        tags:
            - { name: doctrine.event_subscriber, connection: default }
        calls:
            - [ setAnnotationReader, [ @annotation_reader ] ]
            - [ setDefaultLocale, [ %locale% ] ]
            - [ setTranslatableLocale, [ %locale% ]]
            - [ setTranslationFallback, [ false ] ]

    gedmo.listener.timestampable:
        class: Gedmo\Timestampable\TimestampableListener
        tags:
            - { name: doctrine.event_subscriber, connection: default }
        calls:
            - [ setAnnotationReader, [ @annotation_reader ] ]

    gedmo.listener.sluggable:
        class: Gedmo\Sluggable\SluggableListener
        tags:
            - { name: doctrine.event_subscriber, connection: default }
        calls:
            - [ setAnnotationReader, [ @annotation_reader ] ]

    gedmo.listener.sortable:
        class: Gedmo\Sortable\SortableListener
        tags:
            - { name: doctrine.event_subscriber, connection: default }
        calls:
            - [ setAnnotationReader, [ @annotation_reader ] ]

    gedmo.listener.loggable:
        class: Gedmo\Loggable\LoggableListener
        tags:
            - { name: doctrine.event_subscriber, connection: default }
        calls:
            - [ setAnnotationReader, [ @annotation_reader ] ]

Don't forget change 'AppBundle\Listener\DoctrineExtensionListener' to your DoctrineExtensionListener namespace.

Finally, Do not forget to import doctrine_extensions.yml in your app/config/config.yml :

# file: app/config/config.yml
imports:
    - { resource: parameters.yml }
    - { resource: security.yml }
    - { resource: doctrine_extensions.yml }

More details of Gedmo Doctrine2 extensions in Symfony2: https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/symfony2.md

IV. (optional) If you want paginate results with KNPPaginatorBundle you can install and configure it.

More details: https://github.com/KnpLabs/KnpPaginatorBundle#configuration-example

V. Configure the bundle.

V.I. Add kdrmklabs_ticket configuration to you config.yml

# file: app/config/config.yml

kdrmklabs_ticket:
    user_class: AppBundle\Entity\User
    user_primay_key: id

Where user_primay_key is the name of your primary key in the user table.

V.II. Add resolve_target_entities to your doctrine configuration

# file: app/config/config.yml

# Doctrine Configuration
doctrine:
    orm:
        auto_mapping: true
        resolve_target_entities:
            Kdrmklabs\Bundle\TicketBundle\Model\UserInterface: AppBundle\Entity\User

Do not forget change 'AppBundle\Entity\User' to your User Entity namespace

More details about resolve_target_entities: http://symfony.com/doc/current/cookbook/doctrine/resolve_target_entity.html

V.III. Implements Kdrmklabs\Bundle\TicketBundle\Model\UserInterface from your User entity.

// file: 

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * User
 *
 * @ORM\Table(name="user")
 * @ORM\Entity
 */
class User implements \Kdrmklabs\Bundle\TicketBundle\Model\UserInterface
{
    public function getId(){
        
    }
}

VII. Finally, create database tables and update the schema

Update your database schema with the command:

$ php app/console doctrine:schema:update --force

Now, you need to populete the database tables kdrmklabs_ticket_status and kdrmklabs_ticket_category

Example of SQL to populete the tables:

INSERT INTO `kdrmklabs_ticket_category` (`id`, `name`, `active`) VALUES ('1', 'Billing', '1');
INSERT INTO `kdrmklabs_ticket_category` (`id`, `name`, `active`) VALUES ('2', 'Cancellations and refunds', '1');
INSERT INTO `kdrmklabs_ticket_category` (`id`, `name`, `active`) VALUES ('3', 'Report a scam or offer false', '1');
INSERT INTO `kdrmklabs_ticket_category` (`id`, `name`, `active`) VALUES ('4', 'Report inappropriate or illegal content', '1');
INSERT INTO `kdrmklabs_ticket_category` (`id`, `name`, `active`) VALUES ('5', 'Security Center and user protection', '1');

INSERT INTO `kdrmklabs_ticket_status` (`id`, `name`, `active`) VALUES ('1', 'Pending', '1');
INSERT INTO `kdrmklabs_ticket_status` (`id`, `name`, `active`) VALUES ('2', 'Invalid', '1');
INSERT INTO `kdrmklabs_ticket_status` (`id`, `name`, `active`) VALUES ('3', 'Solved', '1');

Statuses

You can add to the database table kdrmklabs_ticket_status all states that you want to use to identify the tickets, for example:

image

Categories

You can add to the database table kdrmklabs_ticket_category all categories that you want to use to classify the tickets, for example:

image

Available services

Examples of implementation of kdrmklabs_ticket.ticket_service from a controller

Create a ticket

/**
* @Route("/create")
*/
public function createAction() {
   $kdrmklabs_ticket_service = $this->get('kdrmklabs_ticket.ticket_service');
   $ticket = $kdrmklabs_ticket_service->createTicket("message", "subject", 1, 1, 1);

   return $this->redirectToRoute('kdrmklabs_ticket_show', array('id' => $ticket->getId()));
}

createTicket -> Return a Kdrmklabs\Bundle\TicketBundle\Entity\Ticket object

Description

createTicket(
    string $initial_message, 
    string $subject, 
    int|Kdrmklabs\Bundle\TicketBundle\Model\UserInterface|AppBundle\Entity\User $user, 
    int|Kdrmklabs\Bundle\TicketBundle\Entity\TicketCategory $category,
    int|Kdrmklabs\Bundle\TicketBundle\Entity\TicketStatus $status 
    [, int $dateAdded])

Delete

/**
* @Route("/delete/{id}")
*/
public function deleteAction($id) {
   $kdrmklabs_ticket_service = $this->get('kdrmklabs_ticket.ticket_service');
   $kdrmklabs_ticket_service->deleteTicket($id);

   return $this->redirectToRoute('kdrmklabs_ticket_index');
}

deleteTicket -> Return boolean

Description:

deleteTicket( int|Kdrmklabs\Bundle\TicketBundle\Entity\Ticket $ticket)

Reply

/**
* @Route("/reply/{id}")
*/
public function replyAction($id) {
   $kdrmklabs_ticket_service = $this->get('kdrmklabs_ticket.ticket_service');
   $ticket = $kdrmklabs_ticket_service->replyTicket($id, 1, "reply");

   return $this->redirectToRoute('kdrmklabs_ticket_show', array('id' => $ticket->getId()));
}

replyTicket -> Return a Kdrmklabs\Bundle\TicketBundle\Entity\Ticket object

Description:

replyTicket(
    int|Kdrmklabs\Bundle\TicketBundle\Entity\Ticket $ticket, 
    int|Kdrmklabs\Bundle\TicketBundle\Model\UserInterface|AppBundle\Entity\User $user, 
    string $message
    [, int $dateAdded])

get a ticket

/**
* @Route("/show/{id}", name="kdrmklabs_ticket_show")
* @Template()
*/
public function showAction($id) {
   $kdrmklabs_ticket_service = $this->get('kdrmklabs_ticket.ticket_service');
   $ticket = $kdrmklabs_ticket_service->getTicket($id);

   return array('ticket' => $ticket);
}

getTicket -> Return a Kdrmklabs\Bundle\TicketBundle\Entity\Ticket object

Description:

getTicket( int|string $id_ticket )

list tickets

/**
* @Route("/", name="kdrmklabs_ticket_index")
* @Template()
*/
public function indexAction(Request $request) {
   $kdrmklabs_ticket_service = $this->get('kdrmklabs_ticket.ticket_service');
   $query = $kdrmklabs_ticket_service->getTickets(); // get all tickets

   $paginator = $this->get('knp_paginator');
   $pagination = $paginator->paginate(
       $query->getQuery(),
       $request->query->get('page', 1),
       10
   );

   return array('pagination' => $pagination);
}

getTickets -> return DQL QUERY

Description:

getTickets(
        [ int|Kdrmklabs\Bundle\TicketBundle\Model\UserInterface|AppBundle\Entity\User $author,
        int|Kdrmklabs\Bundle\TicketBundle\Entity\TicketCategory $category,
        int|Kdrmklabs\Bundle\TicketBundle\Entity\TicketStatus $status 
        int $from_datetime,
        int $to_datetime,
        boolean $closed ]
    )

close ticket

/**
* @Route("/close/{id}")
*/
public function closeAction($id){
   $kdrmklabs_ticket_service = $this->get('kdrmklabs_ticket.ticket_service');
   $kdrmklabs_ticket_service->closeTicket($id);

   return $this->redirectToRoute('kdrmklabs_ticket_index');
}

closeTicket -> Return boolean

Description:

closeTicket( id|string $id_ticket )

User repository from UserInterface

// access to UserInterface from controller
$this->get('kdrmklabs_ticket.user_repository')->find($id_user);

Example complete of Controller implementation

namespace Kdrmklabs\Bundle\TicketBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;

class DefaultController extends Controller {

    /**
     * @Route("/", name="kdrmklabs_ticket_index")
     * @Template()
     */
    public function indexAction(Request $request) {
        $kdrmklabs_ticket_service = $this->get('kdrmklabs_ticket.ticket_service');
        $query = $kdrmklabs_ticket_service->getTickets();

        $paginator = $this->get('knp_paginator');
        $pagination = $paginator->paginate(
            $query->getQuery(),
            $request->query->get('page', 1),
            10
        );
        
        return array('pagination' => $pagination);
    }

    /**
     * @Route("/create")
     */
    public function createAction() {
        $id_user = 1;
        $id_category = 1;
        $id_status = 1;
        $kdrmklabs_ticket_service = $this->get('kdrmklabs_ticket.ticket_service');
        $ticket = $kdrmklabs_ticket_service->createTicket("message", "subject", $id_user, $id_category, $id_status);
        
        return $this->redirectToRoute('kdrmklabs_ticket_show', array('id' => $ticket->getId()));
    }

    /**
     * @Route("/show/{id}", name="kdrmklabs_ticket_show")
     * @Template()
     */
    public function showAction($id) {
        $kdrmklabs_ticket_service = $this->get('kdrmklabs_ticket.ticket_service');
        $ticket = $kdrmklabs_ticket_service->getTicket($id);

        return array('ticket' => $ticket);
    }

    /**
     * @Route("/reply/{id}")
     */
    public function replyAction($id) {
        $id_user = 1;
        $kdrmklabs_ticket_service = $this->get('kdrmklabs_ticket.ticket_service');
        $ticket = $kdrmklabs_ticket_service->replyTicket($id, $id_user, "reply");
        
        return $this->redirectToRoute('kdrmklabs_ticket_show', array('id' => $ticket->getId()));
    }

    /**
     * @Route("/delete/{id}")
     */
    public function deleteAction($id) {
        $kdrmklabs_ticket_service = $this->get('kdrmklabs_ticket.ticket_service');
        $kdrmklabs_ticket_service->deleteTicket($id);
        
        return $this->redirectToRoute('kdrmklabs_ticket_index');
    }

    /**
     * @Route("/close/{id}")
     */
    public function closeAction($id){
        $kdrmklabs_ticket_service = $this->get('kdrmklabs_ticket.ticket_service');
        $kdrmklabs_ticket_service->closeTicket($id);
        
        return $this->redirectToRoute('kdrmklabs_ticket_index');
    }
}