Skip to content

Commit

Permalink
Added a sorter for post hydration sorting.
Browse files Browse the repository at this point in the history
Added notes to readme.
  • Loading branch information
Mark Ogilvie committed May 7, 2018
1 parent 31863bf commit f618a9a
Show file tree
Hide file tree
Showing 5 changed files with 197 additions and 43 deletions.
26 changes: 0 additions & 26 deletions Doctrine/ORM/GdprEntityManager.php

This file was deleted.

152 changes: 150 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ use SpecShaper\GdprBundle\Form\Type\PersonalDataType;
;
```

## Step 5: Decode in templates
## Step 5: Decrypt in templates
To view your data in a twig template:
```
{{ employee.bankAccount.iban }}
Expand Down Expand Up @@ -308,7 +308,155 @@ Todo: Use the twig_filter for personal_data to pass rendering options:
{{ employee.height.data | personal_data("decimal", 2) }}
```

## Step 6: Reporting
## Step 6: Decrypt in repository
The problem with encrypting data in the DB is that it can no longer be used
for ordering or searching.

We use a trait in the entity repositories to provide common functions for dealing
with PersonalData objects.

```php
<?php
// src/AppBundle/Repository/Traits/GdprTrait.php

namespace AppBundle\Repository\Traits;

use SpecShaper\EncryptBundle\Encryptors\EncryptorInterface;
use SpecShaper\GdprBundle\Utils\Sorter;

/**
* Trait GdprTrait
*
* Trait to provide common functions for encrypted fields in a repository.
* - Decrypt & concatenate first and last name
* - Sort by two fields.
*
* @package AppBundle\Repository\Traits
*/
trait GdprTrait
{
/**
* @var EncryptorInterface
*/
protected $encryptor;

/**
* Setter injection Encryptor into repository.
*
* @param EncryptorInterface $encryptor
* @return $this
*/
public function setEncryptor(EncryptorInterface $encryptor){
$this->encryptor = $encryptor;
return $this;
}

/**
* Get the Encryptor
*
* @return EncryptorInterface
*/
public function getEncryptor()
{
return $this->encryptor;
}

/**
* Function to concat two encrypted values into one new value.
*
* @param array $collection
* @param string $firstNameField Default is firstName
* @param string $lastNameField Default is lastName
* @param string $outputField Default is fullName
* @return array
*/
public function concatToFullName(&$collection, $firstNameField = 'firstName', $lastNameField = 'lastName', $outputField = 'fullName'){

foreach($collection as $key => $entity){
$firstName = $this->getEncryptor()->decrypt($entity[$firstNameField]->getData());
$lastName = $this->getEncryptor()->decrypt($entity[$lastNameField]->getData());
$collection[$key][$firstNameField]->setData($firstName);
$collection[$key][$lastNameField]->setData($lastName);
$collection[$key][$outputField] = $firstName . ' ' . $lastName;
}

return $collection;
}

/**
* Sort a array hydrated query result by two columns.
*
* @param array $result
* @param string $firstOrder
* @param string $secondOrder
* @return mixed
*/
public function sortByTwoColumns(&$result, $firstOrder = 'employeeId', $secondOrder = 'lastName')
{
// Use SpecShaper\ThemeBundle\Util\Sorter:sortByTwoColumnsCallback as a callback
usort($result, array(new Sorter($firstOrder, $secondOrder),'sortByTwoColumnsCallback'));

return $result;
}
}
```
The trait is used in the repository.

```php
<?php

namespace AppBundle\Repository;

use AppBundle\Repository\Traits\GdprTrait;

/**
* EmployeeRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class EmployeeRepository extends \Doctrine\ORM\EntityRepository
{
use GdprTrait;


//....

}
```
In inject the encryptor into the repository in the controller.
```
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\Employee;
use SpecShaper\EncryptBundle\Encryptors\EncryptorInterface;
/**
* Employee controller.
*
* @Route("/employee")
*/
class EmployeeController extends Controller
{
/**
* Lists all Employee entities.
*
* @Route("/{id}/all", name="employee_all")
* @Method("GET")
*/
public function allAction(EncryptorInterface $encryptor)
{
$employee = $this->getDoctrine()->getRepository(Employee::class)
->setEncryptor($encryptor)
->findAll();
}
}
```

## Step 7: Reporting

### Coverage Report

Expand Down
22 changes: 7 additions & 15 deletions Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ services:
resource: '../../*'
# you can exclude directories or files
# but if a service is unused, it's removed anyway
exclude: '../../{Entity,Repository,Tests,Entity/*}'
exclude: '../../{Entity,Repository}'

SpecShaper\GdprBundle\Controller\:
resource: '../../Controller'
public: true
tags: ['controller.service_arguments']

# Subscriber Interface, default is DoctrineEncryptSubscriber but it can be overriden in the app config
SpecShaper\GdprBundle\Subscribers\GdprSubscriber:
Expand All @@ -27,17 +32,4 @@ services:
arguments:
$encryptionDisabled: '%spec_shaper_encrypt.is_disabled%'
tags:
- { name: 'console.command', command: 'gdpr:update' }

SpecShaper\GdprBundle\Doctrine\ORM\GdprEntityManager:
decorates: Doctrine\ORM\EntityManagerInterface
arguments: ['@SpecShaper\GdprBundle\Doctrine\ORM\GdprEntityManager.inner','@SpecShaper\EncryptBundle\Encryptors\EncryptorInterface']

# # Subscriber Interface, default is DoctrineEncryptSubscriber but it can be overriden in the app config
# SpecShaper\EncryptBundle\Subscribers\DoctrineEncryptSubscriberInterface:
# class: '%spec_shaper_encrypt.subscriber_class%'
# arguments:
# $annotationArray: '%spec_shaper_encrypt.annotation_classes%'
# $isDisabled: '%spec_shaper_encrypt.is_disabled%'
# tags:
# - { name: doctrine.event_subscriber, connection: default }
- { name: 'console.command', command: 'gdpr:update' }
2 changes: 2 additions & 0 deletions Subscribers/GdprSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,9 @@ public function postFlush(PostFlushEventArgs $args)
/**
* Listen a postLoad lifecycle event.
* Decrypt any personal_data if it is encrytpted.
*
* @param LifecycleEventArgs $args
* @throws GdprException
*/
public function postLoad(LifecycleEventArgs $args)
{
Expand Down
38 changes: 38 additions & 0 deletions Utils/Sorter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace SpecShaper\GdprBundle\Utils;

class Sorter
{
public $firstSortOrder;

protected $secondSortOrder;

public function __construct($firstSortOrder, $secondSortOrder)
{
$this->firstSortOrder = $firstSortOrder;
$this->secondSortOrder = $secondSortOrder;
}

/**
* Order an array by two fields,
*
* @param $a
* @param $b
* @return int
*/
public function sortByTwoColumnsCallback($a, $b){

$firstOrder = $this->firstSortOrder;

$secondOrder= $this->secondSortOrder;

if ($a[$firstOrder] == $b[$firstOrder])
{
// employeeId is the same, sort by lastName
if ($a[$secondOrder] > $b[$secondOrder]) return 1;
}
// sort the higher employeeId first:
return $a[$firstOrder] < $b[$firstOrder] ? 1 : -1;
}
}

0 comments on commit f618a9a

Please sign in to comment.