diff --git a/README.md b/README.md
index 6543cf6..a52dc68 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,15 @@
-# Pager bundle
+# Audit bundle
-This paginator is different in the following ways:
+This bundle creates an audit log for all doctrine ORM database related changes:
-- it has only one general class with around 300 lines of commented code. All the rest of the source code is
-specific to symfony2 framework, twig helpers and templates.
-- it allows to create custom pagination filters - search, select.. and modify the database query based on the specific
-use cases.
-- it also handles sorting in the traditional way.
-- it is very small and may be reused in other frameworks with the modifications needed.
-- it can only paginate Doctrine2 ORM QueryBuilder. Nothing else will be supported to maintain this library small and
-backward compatible. For your own customizations just fork or copy the source code.
-- there may be only one pagination per request, because url query parameters are constant.
+- inserts and updates including their diffs and relation field diffs.
+- many to many relation changes, association and dissociation actions.
+- if there is an user in token storage, it will link him to the log.
+
+Basically you can track any change from these log entries if they were
+managed through standard **ORM** operations.
+
+**NOTE:** audit cannot track DQL or direct SQL updates or delete statement executions.
## Demo
@@ -19,8 +18,7 @@ and run:
make
-Visit **http://localhost:8000** to see the paginated fake projects with custom
-filters and sorters.
+Visit **http://localhost:8000/audit** to see the log actions.
The demo application source is available in **example** directory and it is a basic
symfony application.
@@ -29,349 +27,22 @@ symfony application.
First, install it with composer:
- composer require data-dog/pager-bundle
+ composer require data-dog/audit-bundle
Then, add it in your **AppKernel** bundles.
## Usage
-The general usage example in your controller:
-
-``` php
-getDoctrine()->getManager()->getRepository("AppBundle:Project")
- ->createQueryBuilder('p')
- ->addSelect('l')
- ->innerJoin('p.language', 'l');
-
- $projects = new Pagination($qb, $request);
- return compact('projects');
- }
-}
-```
-
-All you need is to construct Pagination with [doctrine query builder](http://doctrine-orm.readthedocs.org/en/latest/reference/query-builder.html) and
-the request. The **Pagination** object acts like an array, so you can pass it to the view and iterate over paginated items.
-
-The view:
-
-``` twig
-
-
-
- # |
- {{ sorter_link(projects, "p.code", "Code") }} |
- {{ sorter_link(projects, "p.name", "Name") }} |
- {{ sorter_link(projects, "p.hoursSpent", "Hours Spent") }} |
- {{ sorter_link(projects, "l.code", "Language") }} |
-
-
-
- {% for project in projects %}
-
- {{ project.id }} |
- {{ project.code }} |
- {{ project.name }} |
- {% if project.isOverDeadline %}
- {{ project.hoursSpent }} |
- {% else %}
- {{ project.hoursSpent }} |
- {% endif %}
- {{ project.language.code }} |
-
- {% endfor %}
-
-
-
-
-```
-
-There are **twig** helper functions used:
-
-- **sorter_link** - which uses the twig template to generate a link with the sorting order class and such.
-- **pagination** - which creates a pagination html code to navigate pages.
-
-These templates may be modified in standard symfony ways, see the configuration section.
-
-### Filters
-
-In order to filter paginated results in different kinds of ways, you may extend the code.
-In the controller, provide some pagination options.
-
-``` php
-andWhere($qb->expr()->like('p.name', "'%{$val}%'"));
- } else {
- // this allows us to safely ignore empty values
- // otherwise if $qb is not changed, it would add where the string is empty statement.
- $qb->andWhere('1 = 1');
- }
- break;
- case 'p.hoursSpent':
- switch ($val) {
- case 'lessThan10':
- $qb->andWhere($qb->expr()->lt('p.hoursSpent', $qb->expr()->literal(10)));
- break;
- case 'upTo20':
- $qb->andWhere($qb->expr()->lte('p.hoursSpent', $qb->expr()->literal(20)));
- break;
- case 'moreThan2weeks':
- $qb->andWhere($qb->expr()->gte('p.hoursSpent', $qb->expr()->literal(80)));
- break;
- case 'overDeadline':
- $qb->andWhere($qb->expr()->gt('p.hoursSpent', 'p.deadline'));
- break;
- }
- break;
- }
- }
-
- /**
- * @Method("GET")
- * @Template
- * @Route("/", name="homepage")
- */
- public function indexAction(Request $request)
- {
- $qb = $this->getDoctrine()->getManager()->getRepository("AppBundle:Project")
- ->createQueryBuilder('p')
- ->addSelect('l')
- ->innerJoin('p.language', 'l');
-
- $options = [
- 'sorters' => ['l.code' => 'ASC'], // sorted by language code by default
- 'filters' => ['p.hoursSpent' => 'overDeadline'], // we can apply a filter option by default
- 'applyFilter' => [$this, 'projectFilters'], // custom filter handling
- ];
-
- // our language filter options, the key will be used in where statemt by default
- // and the value as title.
- // The $filterAny key is a placeholder to skip the filter, so the any value could be ok.
- $languages = [
- Pagination::$filterAny => 'Any',
- 'php' => 'PHP',
- 'hs' => 'Haskell',
- 'go' => 'Golang',
- ];
-
- // our spent time filter options, has specific keys so we know how to customize
- $spentTimeGroups = [
- Pagination::$filterAny => 'Any',
- 'lessThan10' => 'Less than 10h',
- 'upTo20' => 'Up to 20h',
- 'moreThan2weeks' => 'More than 2weeks',
- 'overDeadline' => 'Over deadline',
- ];
-
- $projects = new Pagination($qb, $request, $options);
- return compact('projects', 'languages', 'spentTimeGroups');
- }
-}
-```
-
-Now here we have added three filters:
-
-`$languages` and `$spentTimeGroups` will be used as `filter_select` options. The language options are simple and they
-refer to direct values, so the where statement does not need to be modified. But spent time groups are custom so we
-use custom options. In that case we need an `applyFilter` option to be set as a callable so the QueryBuilder could be modified
-accordingly based on our custom options.
-
-So how the view has changed:
-
-``` twig
-
-
-
- # |
- {{ sorter_link(projects, "p.code", "Code") }} |
- {{ sorter_link(projects, "p.name", "Name") }} |
- {{ sorter_link(projects, "p.hoursSpent", "Hours Spent") }} |
- {{ sorter_link(projects, "l.code", "Language") }} |
-
-
-
- |
- |
- {{ filter_search(projects, "p.name") }} |
- {{ filter_select(projects, "p.hoursSpent", spentTimeGroups) }} |
- {{ filter_select(projects, "l.code", languages) }} |
-
-
-
- {% for project in projects %}
-
- {{ project.id }} |
- {{ project.code }} |
- {{ project.name }} |
- {% if project.isOverDeadline %}
- {{ project.hoursSpent }} |
- {% else %}
- {{ project.hoursSpent }} |
- {% endif %}
- {{ project.language.code }} |
-
- {% endfor %}
-
-
-
-
-```
-
-We have used two new twig functions for filters:
-
-- **filter_search** - for searching projects by name.
-- **filter_select** - for basic option filters.
-
-These functions are rendering twig templates for our filters.
-
-### Links
-
-In case if you need to make a link and maintain search filters and sorters applied, use the `$pagination->query()`
-function to get all the necessary url parameters and merge it with your link parameters.
-
-The demo example handles **enable** and **disable** toggling for projects in a separate controller action
-and maintains all pagination properties.
-
-## Configuration
-
-There is no necessary configuration for a general usage. But in order to customize pagination
-there may be global options set in **app.php** for example:
-
-``` php
- 15,
- 'range' => 9,
-]);
-Pagination::$maxPerPage = 200;
-
-require_once __DIR__.'/../app/AppKernel.php';
-
-$kernel = new AppKernel('prod', false);
-$kernel->loadClassCache();
-$request = Request::createFromGlobals();
-$response = $kernel->handle($request);
-$response->send();
-$kernel->terminate($request, $response);
-```
-
-### Templates
-
-The default templates for filters and pagination are based on [twitter bootstrap](http://getbootstrap.com/) and
-[fontawesome](http://fortawesome.github.io/Font-Awesome/). You
-can customize them same as any other bundle template, for example:
-
-- pagination - **app/Resources/DataDogPagerBundle/views/pagination.html.twig**
-- search filter - **app/Resources/DataDogPagerBundle/views/filters/search.html.twig**
-
-### Extending with more filters
-
-The best way to customize your filters is to extend twig extension, or create a new extension.
-If we would provide many options, that would confuse people in the end, so instead we add a little boilerplate.
-In your bundle **services.yml** update parameters:
-
-``` yaml
-parameters:
- datadog.pager.twig_extension.class: AppBundle\Twig\PaginationExtension
-```
-
-Then create a class:
-
-``` php
- ['html'],
- 'needs_environment' => true,
- ];
-
- $funcs = parent::getFunctions();
- $funcs['filter_search_placeholder'] = new \Twig_Function_Method($this, 'filterSearchPlaceholder', $defaults);
-
- return $funcs;
- }
-
- public function filterSearchPlaceholder(\Twig_Environment $twig, Pagination $pagination, $key, $placeholder)
- {
- $value = isset($pagination->query()['filters'][$key]) ? $pagination->query()['filters'][$key] : '';
- return $twig->render('AppBundle::filters/search_placeholder.html.twig', compact('key', 'pagination', 'value', 'placeholder'));
- }
-}
-```
-
-And finally copy and modify the template based on your needs
+**audit** entities will be mapped automatically if you run schema update or similar.
+And all the database changes will be reflected in the audit log afterwards.
## Screenshots
-![Screenshot](https://raw.github.com/DATA-DOG/DataDogPagerBundle/master/screenshots/pagination1.png)
+![Screenshot](https://raw.github.com/DATA-DOG/DataDogAuditBundle/master/screenshots/audit1.png)
+
+![Screenshot](https://raw.github.com/DATA-DOG/DataDogAuditBundle/master/screenshots/audit2.png)
-![Screenshot](https://raw.github.com/DATA-DOG/DataDogPagerBundle/master/screenshots/pagination2.png)
+![Screenshot](https://raw.github.com/DATA-DOG/DataDogAuditBundle/master/screenshots/audit3.png)
## License
diff --git a/screenshots/audit1.png b/screenshots/audit1.png
new file mode 100644
index 0000000..bfebe20
Binary files /dev/null and b/screenshots/audit1.png differ
diff --git a/screenshots/audit2.png b/screenshots/audit2.png
new file mode 100644
index 0000000..4d5964c
Binary files /dev/null and b/screenshots/audit2.png differ
diff --git a/screenshots/audit3.png b/screenshots/audit3.png
new file mode 100644
index 0000000..6f767b6
Binary files /dev/null and b/screenshots/audit3.png differ
diff --git a/src/DataDog/AuditBundle/Resources/config/doctrine/Association.orm.xml b/src/DataDog/AuditBundle/Resources/config/doctrine/Association.orm.xml
new file mode 100644
index 0000000..2f09732
--- /dev/null
+++ b/src/DataDog/AuditBundle/Resources/config/doctrine/Association.orm.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/DataDog/AuditBundle/Resources/config/doctrine/Association.orm.yml b/src/DataDog/AuditBundle/Resources/config/doctrine/Association.orm.yml
new file mode 100644
index 0000000..844722d
--- /dev/null
+++ b/src/DataDog/AuditBundle/Resources/config/doctrine/Association.orm.yml
@@ -0,0 +1,20 @@
+DataDog\AuditBundle\Entity\Association:
+ type: entity
+ table: audit_associations
+ id:
+ id:
+ type: bigint
+ generator:
+ strategy: IDENTITY
+
+ fields:
+ typ:
+ length: 128
+ tbl:
+ length: 128
+ label:
+ nullable: true
+ fk:
+ type: string
+ class:
+ type: string
diff --git a/src/DataDog/AuditBundle/Resources/config/doctrine/AuditLog.orm.xml b/src/DataDog/AuditBundle/Resources/config/doctrine/AuditLog.orm.xml
new file mode 100644
index 0000000..261ae89
--- /dev/null
+++ b/src/DataDog/AuditBundle/Resources/config/doctrine/AuditLog.orm.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/DataDog/AuditBundle/Resources/config/doctrine/AuditLog.orm.yml b/src/DataDog/AuditBundle/Resources/config/doctrine/AuditLog.orm.yml
new file mode 100644
index 0000000..4aaa2cb
--- /dev/null
+++ b/src/DataDog/AuditBundle/Resources/config/doctrine/AuditLog.orm.yml
@@ -0,0 +1,29 @@
+DataDog\AuditBundle\Entity\AuditLog:
+ type: entity
+ table: audit_logs
+ id:
+ id:
+ type: bigint
+ generator:
+ strategy: IDENTITY
+
+ oneToOne:
+ source:
+ targetEntity: Association
+ joinColumn:
+ nullable: false
+ target:
+ targetEntity: Association
+ blame:
+ targetEntity: Association
+
+ fields:
+ action:
+ length: 12
+ tbl:
+ length: 128
+ diff:
+ type: json_array
+ nullable: true
+ loggedAt:
+ type: datetime