Using An object method as a callable to update GDPR consent in DB
Tags: howto, php, tutorial
Published: 2023-08-18 13:53:03

During development I has a case, where via a Mailing List api in order to retrieve the deleted subscribers from it.

```php

// Making an Adapter (using GoF adapter pattern)
class MailingListApiAdapter
{
   const USUBBED_SUBSCRIBERS='....';
   const DELETED_SUBSCRIBERS='....';
   // Some Implementation here
   
   public function retrieveUnsubbedUsers(int $page=1,int &$total_pages): array
   {
      return $this->retrieveSubscribers(USUBBED_SUBSCRIBERS,$page,$total_pages);
   }

   
   public function retrieveDeletedUsers(int $page=1,int &$total_pages): array
   {
      return $this->retrieveSubscribers(DELETED_SUBSCRIBERS,$page,$total_pages);
   }

   private function retrieveSubscribers(string $url,int $page=1,int &$total_pages):array
   {
     // call api and return array
   }
}

```
And due to a misconfig I had the stored consent of a user not on par with the mailing list. Therefore, I wanted to scan the existing lists and update the consent into the DB.

The consents is into the following RDBS table (I know it is not a specific db I try to keep db agnostic) :

```
user_consent
----
int user_id
string email
string consent_type
bool given_consent
```

And I did the following script to update the consent:

```php

function scan_list_and_update_consent(\PDO $dbh, Callable $retreiveSubscribersFunction){
  
   $dbh
   $page=1;
   $total_pages = 1;
      $sql = "UPDATE user_consent SET given_consent=false where consent_type='MAILING_LIST' and given_consent=true and email = :email";   
     $stmt = $dbh->prepare($sql);
   do {
     $dbh->beginTransaction();
     try{
       $subs = $retreiveSubscribersFunction($page,$limit);
       foreach($subs as $email){
          $stmt->execute(['email'=>$email]);  
       }
       $dbh->commit();
     } catch(\Exception $e){
       $dbh->rollback();
     }
     $page++;
     // We slow down the request frequency in order to avoid throttling
     sleep(3); 
   }while($page<=$total_pages);
  
}
```

As you can see we iterate all pages (The second param is passed by reference  at both `retrieveUnsubbedUsers`and `retrieveDeletedUsers` so we can retrieve the total pages with a singlwe request)

Also we assume that both  both `retrieveUnsubbedUsers`and `retrieveDeletedUsers` return an array with emails:

```
[
  'user@example.com',
   ....
]
```

So having a known logic we can call the function `scan_list_and_update_consent` like this:

```php
function scan_list_and_update_consent(\PDO $dbh, Callable $retreiveSubscribersFunction){
  
   $dbh
   $page=1;
   $total_pages = 1;
      $sql = "UPDATE user_consent SET given_consent=false where consent_type='MAILING_LIST' and given_consent=true and email = :email";   
     $stmt = $dbh->prepare($sql);
   do {
     $dbh->beginTransaction();
     try{
       $subs = $retreiveSubscribersFunction($page,$limit);
       foreach($subs as $email){
          $stmt->execute(['email'=>$email]);  
       }
       $dbh->commit();
     } catch(\Exception $e){
       $dbh->rollback();
     }
     $page++;
     // We slow down the request frequency in order to avoid throttling
     sleep(3); 
   }while($page<=$total_pages);
  
}

$pdo = new PDO();
// Config pdo futher here

$mailingList = new MailingListApiAdapter();

// call $mailinglist->retrieveDeletedUsers
scan_list_and_update_consent($pdo,[$mailingList,'retrieveDeletedUsers']);


// call $mailinglist->retrieveUnsubbedUsers
scan_list_and_update_consent($pdo,[$mailingList,'retrieveUnsubbedUsers']);
```

So as a result we can use functions with same signature (aka same parameter number and types) with a single piece of code avoiding duplicate code.

In our case we can add another function (eg. `getNonReceivingEmailSubscribers`) with same parameters ($page and $total_pages) and still be able to use it with `scan_list_and_update_consent`:

```php
// call $mailinglist->retrieveUnsubbedUsers
scan_list_and_update_consent($pdo,[$mailingList,'getNonReceivingEmailSubscribers']);
```

And we do not need to re-write the loop and the logic for fetching all pages for api calls.