Developed with love by KnpLabs Hire us for your project!
28

ActivityLogBundle

by madmis

Activity log bundle - Extended doctrine loggable bundle

Symfony ActivityLog Component

SensioLabsInsight
Build Status
Coverage Status
Latest Stable Version
Total Downloads
License

ActivityLogBundle - Extended doctrine loggable (StofDoctrineExtensionsBundle)

What's inside

ActivityLogBundle uses Loggable extension from StofDoctrineExtensionsBundle and DoctrineExtensions

This bundle extend Gedmo\Loggable\Entity\MappedSuperclass\AbstractLogEntry with below fields:

  • parentId - store depedency to "main entity"
  • parentClass - store "main entity" type
  • oldData - data that were changed
  • name - entry name (to show in activity log)
  • user - associations mapping with user who changed data

Bundle contain extended listener (**LoggableListener**) to process above fields.

Also available formatter to preprocessing activity log before show in view (html).

Installation

Pretty simple with Composer, run:

composer require madmis/activity-log-bundle

Then enable the bundle in the kernel:

public function registerBundles()
{
    $bundles = [
        // ...
        new ActivityLogBundle\ActivityLogBundle(),
        // ...
    ];
    ...
}

Configure bundle:

# app/config/config.yml
doctrine:
    dbal:
        #...
    orm:
        #...
        resolve_target_entities:
            Symfony\Component\Security\Core\User\UserInterface: AppBundle\Entity\User
        mappings:
            gedmo_loggable:
                type: annotation
                prefix: Gedmo\Loggable\Entity
                dir: "%kernel.root_dir%/../src/AppBundle/Entity/"
                alias: GedmoLoggable
                is_bundle: false

stof_doctrine_extensions:
    class:
        loggable: ActivityLogBundle\Listener\LoggableListener
    orm:
        default:
            loggable: true

Create entity and make it loggable:


namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use ActivityLogBundle\Entity\Interfaces\StringableInterface;

/**
 * @package AppBundle\Entity
 * @ORM\Entity(repositoryClass="ProjectRepository")
 * @ORM\Table
 * @Gedmo\Loggable(logEntryClass="ActivityLogBundle\Entity\LogEntry")
 */
class Project implements StringableInterface
{
    /**
     * @var int
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     * @ORM\Column(type="string", length=128)
     * @Gedmo\Versioned
     */
    private $name;

    /**
     * @var string
     * @ORM\Column(type="string", length=16)
     * @Gedmo\Versioned
     */
    private $key;

    //...

StringableInterface required to save LogEntry::name.

Then run command to update database schema:

php bin/console doctrine:schema:update --force

Using formatter to data view

Formatter class: ActivityLogBundle\Service\ActivityLog\ActivityLogFormatter
Formatter service: activity_log.formatter

required: LoggerInterface as dependency

By default entity without custom formatter class formatted by ActivityLogBundle\Service\ActivityLog\EntityFormatter\UniversalFormatter

But you can implement custom formatter for each entity.

To register a custom formatter, add a service tag with the following (required) properties:
* name: 'activity_log.formatter'
* entity: Class name of the entity that should be formatted by the registered formatter

Example:
php
services:
app.formatter.project:
class: AppBundle\Service\ActivityFormatter\Project
tags:
- { name: activity_log.formatter, entity: 'Project'}

As example formatter for AppBundle\Entity\Project entity:


namespace AppBundle\Service\ActivityFormatter;

class Project extends AbstractFormatter implements FormatterInterface
{
    /**
     * @param LogEntryInterface $log
     * @return array
     */
    public function format(LogEntryInterface $log)
    {
        $result = $log->toArray();

        if ($log->isCreate()) {
            $result['message'] = sprintf('The <b>Project <span class="font-green-jungle">"%s"</span></b> was created.', $log->getName());
        } else if ($log->isRemove()) {
            $result['message'] = sprintf('The <b>Project <span class="font-red-flamingo">"%s"</span></b> was removed.', $log->getName());
        } else if ($log->isUpdate()) {
            $result['message'] = '<dl><dt>The <b>Project <span class="font-yellow-gold">"%s"</span></b> was updated.</dt>%s</dl>';
            $data = $log->getData();
            $oldData = $log->getOldData();

            $text = '';
            foreach ($data as $field => $value) {
                $value = $this->normalizeValue($field, $value);

                if (array_key_exists($field, $oldData)) {
                    $oldValue = $this->normalizeValue($field, $oldData[$field]);
                    $subText = sprintf('from "<b>%s</b>" to "<b>%s</b>".', $oldValue, $value);
                } else {
                    $subText = sprintf('to "<b>%s</b>".', $value);
                }
                $text .= sprintf('<dd>Property "<b>%s</b>" was changed: %s</dd>', $field, $subText);
            }

            $result['message'] = sprintf($result['message'], $log->getName(), $text);
        } else {
            $result['message'] = "Undefined action: {$log->getAction()}.";
        }

        return $result;
    }
}

If entity has association with other entity it can be resolved by AbstractFormatter::normalizeValue.
This method call method from the entity formatter class, which named as appropriate property.

For example, Project entity has association mapping ManyToOne to Type entity.
To get Type name we can add method type to Project formatter:

namespace AppBundle\Service\ActivityFormatter;

class Project extends AbstractFormatter implements FormatterInterface
{
    //...

    /**
     * @param array $value
     * @return string
     */
    protected function type(array $value)
    {
        if (isset($value['id'])) {
            /** @var Type $entity */
            $entity = $this->entityManager->getRepository('AppBundle:Type')
                ->find($value['id']);

            if ($entity) {
                return $entity->getName();
            }
        }

        return '';
    }

As result we have formatted response to show in view.

Using activity log in controller

$em = $this->getDoctrine()->getManager();
// get log entries for entity
$entries =  $em
            ->getRepository('AppBundle:LogEntry')
            ->getLogEntriesQueryBuilder($entity)
           ->getQuery()
          ->getResult();
// format log entries to show in the view
$entries = $this
            ->get('activity_log.formatter')
            ->format($entries);

For $entity should be configured Entity formatter.

The MIT License (MIT)

Copyright (c) 2016 Symfony Bundles (Dmitry Khaperets)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
activity_log:         []
  • fix buid
    By Dmitry Machin, 1 year ago
  • Merge pull request #10 from WybrenKoelmans/patch-1
    By web-flow, 1 year ago
  • Update AbstractLogEntry.php
    By web-flow, 1 year ago
  • Merge pull request #9 from TwanVermeulen/meta-conflict
    By web-flow, 2 years ago
  • Fixed incorrect meta overwrite
    By , 2 years ago
  • Merge pull request #8 from madmis/delete-save-more-data
    By web-flow, 2 years ago
  • save more data for deleted entries
    By Dmitry Machin, 2 years ago
  • Merge pull request #6 from TwanVermeulen/master
    By web-flow, 2 years ago
  • Allow null value for user property since it's nullable
    By TwanVermeulen, 2 years ago
  • Merge pull request #5 from TwanVermeulen/master
    By web-flow, 2 years ago
  • Fixed incorrect meta overwrite
    By TwanVermeulen, 2 years ago
  • Merge branch 'master' of https://github.com/madmis/ActivityLogBundle
    By Dmitry Machin, 2 years ago
  • fix codestyle
    By Dmitry Machin, 2 years ago
  • fix codestyle
    By Dmitry Machin, 2 years ago
  • fix codestyle
    By Dmitry Machin, 2 years ago
  • Merge pull request #4 from TwanVermeulen/master
    By web-flow, 2 years ago
  • Load custom formatters via service tags instead of path prefix
    By TwanVermeulen, 2 years ago
  • update docs
    By Dmitry Machin, 2 years ago
  • fix code style
    By Dmitry Machin, 3 years ago
  • more test
    By Dmitry Machin, 3 years ago
  • more tests
    By Dmitry Machin, 3 years ago
  • more tests
    By Dmitry Machin, 3 years ago
  • more tests
    By Dmitry Machin, 3 years ago
  • more tests
    By Dmitry Machin, 3 years ago
  • more tests
    By Dmitry Machin, 3 years ago
  • add coverage badge
    By Dmitry Machin, 3 years ago
  • fix travis config
    By Dmitry Machin, 3 years ago
  • fix travis config
    By Dmitry Machin, 3 years ago
  • fix travis config
    By Dmitry Machin, 3 years ago
  • fix travis config
    By Dmitry Machin, 3 years ago