diff --git a/composer.lock b/composer.lock
index 0c8514b945ace5ebb9e720450585f7d3023e946a..464fd3d9ead5c9eb45c4d675936d88954e6960b4 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,20 +4,20 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "c3bebdab1f5cd01e6c038f6defc7bdbe",
+    "content-hash": "b5ae8ece34266c0c406aaf2c5765e2a2",
     "packages": [
         {
             "name": "composer/ca-bundle",
-            "version": "1.3.2",
+            "version": "1.3.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/composer/ca-bundle.git",
-                "reference": "fd5dd441932a7e10ca6e5b490e272d34c8430640"
+                "reference": "30897edbfb15e784fe55587b4f73ceefd3c4d98c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/composer/ca-bundle/zipball/fd5dd441932a7e10ca6e5b490e272d34c8430640",
-                "reference": "fd5dd441932a7e10ca6e5b490e272d34c8430640",
+                "url": "https://api.github.com/repos/composer/ca-bundle/zipball/30897edbfb15e784fe55587b4f73ceefd3c4d98c",
+                "reference": "30897edbfb15e784fe55587b4f73ceefd3c4d98c",
                 "shasum": ""
             },
             "require": {
@@ -64,7 +64,7 @@
             "support": {
                 "irc": "irc://irc.freenode.org/composer",
                 "issues": "https://github.com/composer/ca-bundle/issues",
-                "source": "https://github.com/composer/ca-bundle/tree/1.3.2"
+                "source": "https://github.com/composer/ca-bundle/tree/1.3.3"
             },
             "funding": [
                 {
@@ -80,28 +80,102 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-24T11:56:16+00:00"
+            "time": "2022-07-20T07:14:26+00:00"
+        },
+        {
+            "name": "composer/class-map-generator",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/composer/class-map-generator.git",
+                "reference": "1e1cb2b791facb2dfe32932a7718cf2571187513"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/composer/class-map-generator/zipball/1e1cb2b791facb2dfe32932a7718cf2571187513",
+                "reference": "1e1cb2b791facb2dfe32932a7718cf2571187513",
+                "shasum": ""
+            },
+            "require": {
+                "composer/pcre": "^2 || ^3",
+                "php": "^7.2 || ^8.0",
+                "symfony/finder": "^4.4 || ^5.3 || ^6"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "^1.6",
+                "phpstan/phpstan-deprecation-rules": "^1",
+                "phpstan/phpstan-phpunit": "^1",
+                "phpstan/phpstan-strict-rules": "^1.1",
+                "symfony/filesystem": "^5.4 || ^6",
+                "symfony/phpunit-bridge": "^5"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Composer\\ClassMapGenerator\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "https://seld.be"
+                }
+            ],
+            "description": "Utilities to scan PHP code and generate class maps.",
+            "keywords": [
+                "classmap"
+            ],
+            "support": {
+                "issues": "https://github.com/composer/class-map-generator/issues",
+                "source": "https://github.com/composer/class-map-generator/tree/1.0.0"
+            },
+            "funding": [
+                {
+                    "url": "https://packagist.com",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/composer",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-06-19T11:31:27+00:00"
         },
         {
             "name": "composer/composer",
-            "version": "2.3.7",
+            "version": "2.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/composer/composer.git",
-                "reference": "10cd375cf85dede2ff417ceab517ef9a0dc55407"
+                "reference": "026d6de6ea2c913974a7756661a3faac135cb36e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/composer/composer/zipball/10cd375cf85dede2ff417ceab517ef9a0dc55407",
-                "reference": "10cd375cf85dede2ff417ceab517ef9a0dc55407",
+                "url": "https://api.github.com/repos/composer/composer/zipball/026d6de6ea2c913974a7756661a3faac135cb36e",
+                "reference": "026d6de6ea2c913974a7756661a3faac135cb36e",
                 "shasum": ""
             },
             "require": {
                 "composer/ca-bundle": "^1.0",
+                "composer/class-map-generator": "^1.0",
                 "composer/metadata-minifier": "^1.0",
                 "composer/pcre": "^2 || ^3",
                 "composer/semver": "^3.0",
-                "composer/spdx-licenses": "^1.2",
+                "composer/spdx-licenses": "^1.5.7",
                 "composer/xdebug-handler": "^2.0.2 || ^3.0.3",
                 "justinrainbow/json-schema": "^5.2.11",
                 "php": "^7.2.5 || ^8.0",
@@ -109,7 +183,8 @@
                 "react/promise": "^2.8",
                 "seld/jsonlint": "^1.4",
                 "seld/phar-utils": "^1.2",
-                "symfony/console": "^5.4.7 || ^6.0.7",
+                "seld/signal-handler": "^2.0",
+                "symfony/console": "^5.4.11 || ^6.0.11",
                 "symfony/filesystem": "^5.4 || ^6.0",
                 "symfony/finder": "^5.4 || ^6.0",
                 "symfony/polyfill-php73": "^1.24",
@@ -135,7 +210,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "2.3-dev"
+                    "dev-main": "2.4-dev"
                 },
                 "phpstan": {
                     "includes": [
@@ -174,7 +249,7 @@
             "support": {
                 "irc": "ircs://irc.libera.chat:6697/composer",
                 "issues": "https://github.com/composer/composer/issues",
-                "source": "https://github.com/composer/composer/tree/2.3.7"
+                "source": "https://github.com/composer/composer/tree/2.4.0"
             },
             "funding": [
                 {
@@ -190,7 +265,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-06-06T14:43:28+00:00"
+            "time": "2022-08-16T14:10:48+00:00"
         },
         {
             "name": "composer/metadata-minifier",
@@ -654,16 +729,16 @@
         },
         {
             "name": "doctrine/dbal",
-            "version": "3.3.6",
+            "version": "3.4.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/dbal.git",
-                "reference": "9e7f76dd1cde81c62574fdffa5a9c655c847ad21"
+                "reference": "94e016428884227245fb1219e0de7d8b86ca16d7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/dbal/zipball/9e7f76dd1cde81c62574fdffa5a9c655c847ad21",
-                "reference": "9e7f76dd1cde81c62574fdffa5a9c655c847ad21",
+                "url": "https://api.github.com/repos/doctrine/dbal/zipball/94e016428884227245fb1219e0de7d8b86ca16d7",
+                "reference": "94e016428884227245fb1219e0de7d8b86ca16d7",
                 "shasum": ""
             },
             "require": {
@@ -671,21 +746,21 @@
                 "doctrine/cache": "^1.11|^2.0",
                 "doctrine/deprecations": "^0.5.3|^1",
                 "doctrine/event-manager": "^1.0",
-                "php": "^7.3 || ^8.0",
+                "php": "^7.4 || ^8.0",
                 "psr/cache": "^1|^2|^3",
                 "psr/log": "^1|^2|^3"
             },
             "require-dev": {
                 "doctrine/coding-standard": "9.0.0",
                 "jetbrains/phpstorm-stubs": "2022.1",
-                "phpstan/phpstan": "1.6.3",
-                "phpstan/phpstan-strict-rules": "^1.2",
-                "phpunit/phpunit": "9.5.20",
-                "psalm/plugin-phpunit": "0.16.1",
-                "squizlabs/php_codesniffer": "3.6.2",
-                "symfony/cache": "^5.2|^6.0",
-                "symfony/console": "^2.7|^3.0|^4.0|^5.0|^6.0",
-                "vimeo/psalm": "4.23.0"
+                "phpstan/phpstan": "1.8.2",
+                "phpstan/phpstan-strict-rules": "^1.3",
+                "phpunit/phpunit": "9.5.21",
+                "psalm/plugin-phpunit": "0.17.0",
+                "squizlabs/php_codesniffer": "3.7.1",
+                "symfony/cache": "^5.4|^6.0",
+                "symfony/console": "^4.4|^5.4|^6.0",
+                "vimeo/psalm": "4.24.0"
             },
             "suggest": {
                 "symfony/console": "For helpful console commands such as SQL execution and import of files."
@@ -745,7 +820,7 @@
             ],
             "support": {
                 "issues": "https://github.com/doctrine/dbal/issues",
-                "source": "https://github.com/doctrine/dbal/tree/3.3.6"
+                "source": "https://github.com/doctrine/dbal/tree/3.4.1"
             },
             "funding": [
                 {
@@ -761,7 +836,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-02T17:21:01+00:00"
+            "time": "2022-08-16T18:37:46+00:00"
         },
         {
             "name": "doctrine/deprecations",
@@ -808,34 +883,31 @@
         },
         {
             "name": "doctrine/event-manager",
-            "version": "1.1.1",
+            "version": "1.1.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/event-manager.git",
-                "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f"
+                "reference": "eb2ecf80e3093e8f3c2769ac838e27d8ede8e683"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/event-manager/zipball/41370af6a30faa9dc0368c4a6814d596e81aba7f",
-                "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f",
+                "url": "https://api.github.com/repos/doctrine/event-manager/zipball/eb2ecf80e3093e8f3c2769ac838e27d8ede8e683",
+                "reference": "eb2ecf80e3093e8f3c2769ac838e27d8ede8e683",
                 "shasum": ""
             },
             "require": {
                 "php": "^7.1 || ^8.0"
             },
             "conflict": {
-                "doctrine/common": "<2.9@dev"
+                "doctrine/common": "<2.9"
             },
             "require-dev": {
-                "doctrine/coding-standard": "^6.0",
-                "phpunit/phpunit": "^7.0"
+                "doctrine/coding-standard": "^9",
+                "phpstan/phpstan": "~1.4.10 || ^1.5.4",
+                "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+                "vimeo/psalm": "^4.22"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.0.x-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Doctrine\\Common\\": "lib/Doctrine/Common"
@@ -882,7 +954,7 @@
             ],
             "support": {
                 "issues": "https://github.com/doctrine/event-manager/issues",
-                "source": "https://github.com/doctrine/event-manager/tree/1.1.x"
+                "source": "https://github.com/doctrine/event-manager/tree/1.1.2"
             },
             "funding": [
                 {
@@ -898,20 +970,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-05-29T18:28:51+00:00"
+            "time": "2022-07-27T22:18:11+00:00"
         },
         {
             "name": "gettext/gettext",
-            "version": "v5.6.1",
+            "version": "v5.7.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-gettext/Gettext.git",
-                "reference": "017e249601d32b9a88c2eb4c10eac89bf582a7d3"
+                "reference": "8657e580747bb3baacccdcebe69cac094661e404"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-gettext/Gettext/zipball/017e249601d32b9a88c2eb4c10eac89bf582a7d3",
-                "reference": "017e249601d32b9a88c2eb4c10eac89bf582a7d3",
+                "url": "https://api.github.com/repos/php-gettext/Gettext/zipball/8657e580747bb3baacccdcebe69cac094661e404",
+                "reference": "8657e580747bb3baacccdcebe69cac094661e404",
                 "shasum": ""
             },
             "require": {
@@ -956,7 +1028,7 @@
             "support": {
                 "email": "oom@oscarotero.com",
                 "issues": "https://github.com/php-gettext/Gettext/issues",
-                "source": "https://github.com/php-gettext/Gettext/tree/v5.6.1"
+                "source": "https://github.com/php-gettext/Gettext/tree/v5.7.0"
             },
             "funding": [
                 {
@@ -972,7 +1044,7 @@
                     "type": "patreon"
                 }
             ],
-            "time": "2021-12-04T11:33:21+00:00"
+            "time": "2022-07-27T19:54:55+00:00"
         },
         {
             "name": "gettext/languages",
@@ -1194,16 +1266,16 @@
         },
         {
             "name": "phpmailer/phpmailer",
-            "version": "v6.6.2",
+            "version": "v6.6.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/PHPMailer/PHPMailer.git",
-                "reference": "b52ed06864fdda81b82ec8bf564cf15d45ed4f95"
+                "reference": "9400f305a898f194caff5521f64e5dfa926626f3"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/b52ed06864fdda81b82ec8bf564cf15d45ed4f95",
-                "reference": "b52ed06864fdda81b82ec8bf564cf15d45ed4f95",
+                "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/9400f305a898f194caff5521f64e5dfa926626f3",
+                "reference": "9400f305a898f194caff5521f64e5dfa926626f3",
                 "shasum": ""
             },
             "require": {
@@ -1260,7 +1332,7 @@
             "description": "PHPMailer is a full-featured email creation and transfer class for PHP",
             "support": {
                 "issues": "https://github.com/PHPMailer/PHPMailer/issues",
-                "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.2"
+                "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.6.3"
             },
             "funding": [
                 {
@@ -1268,7 +1340,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2022-06-14T09:27:21+00:00"
+            "time": "2022-06-20T09:21:02+00:00"
         },
         {
             "name": "psr/cache",
@@ -1697,32 +1769,93 @@
             },
             "time": "2021-12-10T11:20:11+00:00"
         },
+        {
+            "name": "seld/signal-handler",
+            "version": "2.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Seldaek/signal-handler.git",
+                "reference": "f69d119511dc0360440cdbdaa71829c149b7be75"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Seldaek/signal-handler/zipball/f69d119511dc0360440cdbdaa71829c149b7be75",
+                "reference": "f69d119511dc0360440cdbdaa71829c149b7be75",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.0"
+            },
+            "require-dev": {
+                "phpstan/phpstan": "^1",
+                "phpstan/phpstan-deprecation-rules": "^1.0",
+                "phpstan/phpstan-phpunit": "^1",
+                "phpstan/phpstan-strict-rules": "^1.3",
+                "phpunit/phpunit": "^7.5.20 || ^8.5.23",
+                "psr/log": "^1 || ^2 || ^3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Seld\\Signal\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "j.boggiano@seld.be",
+                    "homepage": "http://seld.be"
+                }
+            ],
+            "description": "Simple unix signal handler that silently fails where signals are not supported for easy cross-platform development",
+            "keywords": [
+                "posix",
+                "sigint",
+                "signal",
+                "sigterm",
+                "unix"
+            ],
+            "support": {
+                "issues": "https://github.com/Seldaek/signal-handler/issues",
+                "source": "https://github.com/Seldaek/signal-handler/tree/2.0.1"
+            },
+            "time": "2022-07-20T18:31:45+00:00"
+        },
         {
             "name": "simplesamlphp/assert",
-            "version": "v0.2.13",
+            "version": "v0.3.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/simplesamlphp/assert.git",
-                "reference": "72a16329cf95b148717aadd258fbe36ac96cf004"
+                "reference": "9af4fe36e7b2a9c4d0695a8e8ad118c39b9e3564"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/simplesamlphp/assert/zipball/72a16329cf95b148717aadd258fbe36ac96cf004",
-                "reference": "72a16329cf95b148717aadd258fbe36ac96cf004",
+                "url": "https://api.github.com/repos/simplesamlphp/assert/zipball/9af4fe36e7b2a9c4d0695a8e8ad118c39b9e3564",
+                "reference": "9af4fe36e7b2a9c4d0695a8e8ad118c39b9e3564",
                 "shasum": ""
             },
             "require": {
                 "ext-spl": "*",
                 "php": "^7.4 || ^8.0",
-                "webmozart/assert": "^1.10"
+                "webmozart/assert": "^1.11"
             },
             "require-dev": {
-                "simplesamlphp/simplesamlphp-test-framework": "^1.0.5"
+                "simplesamlphp/simplesamlphp-test-framework": "^1.2.1"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "v0.1.x-dev"
+                    "dev-master": "v0.2.x-dev"
                 }
             },
             "autoload": {
@@ -1747,9 +1880,9 @@
             "description": "A wrapper around webmozart/assert to make it useful beyond checking method arguments",
             "support": {
                 "issues": "https://github.com/simplesamlphp/assert/issues",
-                "source": "https://github.com/simplesamlphp/assert/tree/v0.2.13"
+                "source": "https://github.com/simplesamlphp/assert/tree/v0.3.3"
             },
-            "time": "2022-02-17T21:38:59+00:00"
+            "time": "2022-08-02T21:14:39+00:00"
         },
         {
             "name": "simplesamlphp/composer-module-installer",
@@ -1849,16 +1982,16 @@
         },
         {
             "name": "simplesamlphp/simplesamlphp",
-            "version": "v2.0.0-beta.11",
+            "version": "v2.0.0-rc1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/simplesamlphp/simplesamlphp.git",
-                "reference": "9c2ee532a78fecf67d1b985cd79d61a705fb0d9f"
+                "reference": "cf68e6ef7aaecf75d434023adfe30913d993f015"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/simplesamlphp/simplesamlphp/zipball/9c2ee532a78fecf67d1b985cd79d61a705fb0d9f",
-                "reference": "9c2ee532a78fecf67d1b985cd79d61a705fb0d9f",
+                "url": "https://api.github.com/repos/simplesamlphp/simplesamlphp/zipball/cf68e6ef7aaecf75d434023adfe30913d993f015",
+                "reference": "cf68e6ef7aaecf75d434023adfe30913d993f015",
                 "shasum": ""
             },
             "require": {
@@ -1877,8 +2010,8 @@
                 "gettext/translator": "^1.0.1",
                 "php": ">=7.4 || ^8.0",
                 "phpmailer/phpmailer": "^6.5",
-                "simplesamlphp/assert": "^0.2.11",
-                "simplesamlphp/saml2": "^4.5",
+                "simplesamlphp/assert": "^0.3.0",
+                "simplesamlphp/saml2": "^4.6",
                 "symfony/cache": "^5.4",
                 "symfony/config": "^5.4",
                 "symfony/console": "^5.4",
@@ -1901,9 +2034,9 @@
                 "ext-curl": "*",
                 "ext-pdo_sqlite": "*",
                 "mikey179/vfsstream": "~1.6",
-                "simplesamlphp/simplesamlphp-module-adfs": ">=2.0.0-rc1",
-                "simplesamlphp/simplesamlphp-test-framework": "^1.2.0",
-                "simplesamlphp/xml-security": "^0.4.1"
+                "simplesamlphp/simplesamlphp-module-adfs": ">=2.0.0-rc2",
+                "simplesamlphp/simplesamlphp-test-framework": "^1.2.1",
+                "simplesamlphp/xml-security": "^0.4.5"
             },
             "suggest": {
                 "ext-curl": "Needed in order to check for updates automatically",
@@ -1961,20 +2094,20 @@
                 "issues": "https://github.com/simplesamlphp/simplesamlphp/issues",
                 "source": "https://github.com/simplesamlphp/simplesamlphp"
             },
-            "time": "2022-06-04T09:48:27+00:00"
+            "time": "2022-07-01T19:47:50+00:00"
         },
         {
             "name": "symfony/cache",
-            "version": "v5.4.9",
+            "version": "v5.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/cache.git",
-                "reference": "a50b7249bea81ddd6d3b799ce40c5521c2f72f0b"
+                "reference": "5a0fff46df349f0db3fe242263451fddf5277362"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/cache/zipball/a50b7249bea81ddd6d3b799ce40c5521c2f72f0b",
-                "reference": "a50b7249bea81ddd6d3b799ce40c5521c2f72f0b",
+                "url": "https://api.github.com/repos/symfony/cache/zipball/5a0fff46df349f0db3fe242263451fddf5277362",
+                "reference": "5a0fff46df349f0db3fe242263451fddf5277362",
                 "shasum": ""
             },
             "require": {
@@ -2042,7 +2175,7 @@
                 "psr6"
             ],
             "support": {
-                "source": "https://github.com/symfony/cache/tree/v5.4.9"
+                "source": "https://github.com/symfony/cache/tree/v5.4.11"
             },
             "funding": [
                 {
@@ -2058,11 +2191,11 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-21T10:24:18+00:00"
+            "time": "2022-07-28T15:25:17+00:00"
         },
         {
             "name": "symfony/cache-contracts",
-            "version": "v2.5.1",
+            "version": "v2.5.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/cache-contracts.git",
@@ -2121,7 +2254,7 @@
                 "standards"
             ],
             "support": {
-                "source": "https://github.com/symfony/cache-contracts/tree/v2.5.1"
+                "source": "https://github.com/symfony/cache-contracts/tree/v2.5.2"
             },
             "funding": [
                 {
@@ -2141,16 +2274,16 @@
         },
         {
             "name": "symfony/config",
-            "version": "v5.4.9",
+            "version": "v5.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/config.git",
-                "reference": "8f551fe22672ac7ab2c95fe46d899f960ed4d979"
+                "reference": "ec79e03125c1d2477e43dde8528535d90cc78379"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/config/zipball/8f551fe22672ac7ab2c95fe46d899f960ed4d979",
-                "reference": "8f551fe22672ac7ab2c95fe46d899f960ed4d979",
+                "url": "https://api.github.com/repos/symfony/config/zipball/ec79e03125c1d2477e43dde8528535d90cc78379",
+                "reference": "ec79e03125c1d2477e43dde8528535d90cc78379",
                 "shasum": ""
             },
             "require": {
@@ -2200,7 +2333,7 @@
             "description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/config/tree/v5.4.9"
+                "source": "https://github.com/symfony/config/tree/v5.4.11"
             },
             "funding": [
                 {
@@ -2216,20 +2349,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-17T10:39:36+00:00"
+            "time": "2022-07-20T13:00:38+00:00"
         },
         {
             "name": "symfony/console",
-            "version": "v5.4.9",
+            "version": "v5.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "829d5d1bf60b2efeb0887b7436873becc71a45eb"
+                "reference": "535846c7ee6bc4dd027ca0d93220601456734b10"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/829d5d1bf60b2efeb0887b7436873becc71a45eb",
-                "reference": "829d5d1bf60b2efeb0887b7436873becc71a45eb",
+                "url": "https://api.github.com/repos/symfony/console/zipball/535846c7ee6bc4dd027ca0d93220601456734b10",
+                "reference": "535846c7ee6bc4dd027ca0d93220601456734b10",
                 "shasum": ""
             },
             "require": {
@@ -2299,7 +2432,7 @@
                 "terminal"
             ],
             "support": {
-                "source": "https://github.com/symfony/console/tree/v5.4.9"
+                "source": "https://github.com/symfony/console/tree/v5.4.11"
             },
             "funding": [
                 {
@@ -2315,20 +2448,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-18T06:17:34+00:00"
+            "time": "2022-07-22T10:42:43+00:00"
         },
         {
             "name": "symfony/dependency-injection",
-            "version": "v5.4.9",
+            "version": "v5.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dependency-injection.git",
-                "reference": "beecae161577305926ec078c4ed973f2b98880b3"
+                "reference": "a8b9251016e9476db73e25fa836904bc0bf74c62"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/beecae161577305926ec078c4ed973f2b98880b3",
-                "reference": "beecae161577305926ec078c4ed973f2b98880b3",
+                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/a8b9251016e9476db73e25fa836904bc0bf74c62",
+                "reference": "a8b9251016e9476db73e25fa836904bc0bf74c62",
                 "shasum": ""
             },
             "require": {
@@ -2388,7 +2521,7 @@
             "description": "Allows you to standardize and centralize the way objects are constructed in your application",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/dependency-injection/tree/v5.4.9"
+                "source": "https://github.com/symfony/dependency-injection/tree/v5.4.11"
             },
             "funding": [
                 {
@@ -2404,11 +2537,11 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-27T06:40:03+00:00"
+            "time": "2022-07-20T13:00:38+00:00"
         },
         {
             "name": "symfony/deprecation-contracts",
-            "version": "v2.5.1",
+            "version": "v2.5.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/deprecation-contracts.git",
@@ -2455,7 +2588,7 @@
             "description": "A generic function and convention to trigger deprecation notices",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.1"
+                "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2"
             },
             "funding": [
                 {
@@ -2475,16 +2608,16 @@
         },
         {
             "name": "symfony/error-handler",
-            "version": "v5.4.9",
+            "version": "v5.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/error-handler.git",
-                "reference": "c116cda1f51c678782768dce89a45f13c949455d"
+                "reference": "f75d17cb4769eb38cd5fccbda95cd80a054d35c8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/error-handler/zipball/c116cda1f51c678782768dce89a45f13c949455d",
-                "reference": "c116cda1f51c678782768dce89a45f13c949455d",
+                "url": "https://api.github.com/repos/symfony/error-handler/zipball/f75d17cb4769eb38cd5fccbda95cd80a054d35c8",
+                "reference": "f75d17cb4769eb38cd5fccbda95cd80a054d35c8",
                 "shasum": ""
             },
             "require": {
@@ -2526,7 +2659,7 @@
             "description": "Provides tools to manage errors and ease debugging PHP code",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/error-handler/tree/v5.4.9"
+                "source": "https://github.com/symfony/error-handler/tree/v5.4.11"
             },
             "funding": [
                 {
@@ -2542,7 +2675,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-21T13:57:48+00:00"
+            "time": "2022-07-29T07:37:50+00:00"
         },
         {
             "name": "symfony/event-dispatcher",
@@ -2631,7 +2764,7 @@
         },
         {
             "name": "symfony/event-dispatcher-contracts",
-            "version": "v2.5.1",
+            "version": "v2.5.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher-contracts.git",
@@ -2690,7 +2823,7 @@
                 "standards"
             ],
             "support": {
-                "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.1"
+                "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.2"
             },
             "funding": [
                 {
@@ -2710,16 +2843,16 @@
         },
         {
             "name": "symfony/filesystem",
-            "version": "v5.4.9",
+            "version": "v5.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/filesystem.git",
-                "reference": "36a017fa4cce1eff1b8e8129ff53513abcef05ba"
+                "reference": "6699fb0228d1bc35b12aed6dd5e7455457609ddd"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/filesystem/zipball/36a017fa4cce1eff1b8e8129ff53513abcef05ba",
-                "reference": "36a017fa4cce1eff1b8e8129ff53513abcef05ba",
+                "url": "https://api.github.com/repos/symfony/filesystem/zipball/6699fb0228d1bc35b12aed6dd5e7455457609ddd",
+                "reference": "6699fb0228d1bc35b12aed6dd5e7455457609ddd",
                 "shasum": ""
             },
             "require": {
@@ -2754,7 +2887,7 @@
             "description": "Provides basic utilities for the filesystem",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/filesystem/tree/v5.4.9"
+                "source": "https://github.com/symfony/filesystem/tree/v5.4.11"
             },
             "funding": [
                 {
@@ -2770,20 +2903,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-20T13:55:35+00:00"
+            "time": "2022-07-20T13:00:38+00:00"
         },
         {
             "name": "symfony/finder",
-            "version": "v5.4.8",
+            "version": "v5.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/finder.git",
-                "reference": "9b630f3427f3ebe7cd346c277a1408b00249dad9"
+                "reference": "7872a66f57caffa2916a584db1aa7f12adc76f8c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/finder/zipball/9b630f3427f3ebe7cd346c277a1408b00249dad9",
-                "reference": "9b630f3427f3ebe7cd346c277a1408b00249dad9",
+                "url": "https://api.github.com/repos/symfony/finder/zipball/7872a66f57caffa2916a584db1aa7f12adc76f8c",
+                "reference": "7872a66f57caffa2916a584db1aa7f12adc76f8c",
                 "shasum": ""
             },
             "require": {
@@ -2817,7 +2950,7 @@
             "description": "Finds files and directories via an intuitive fluent interface",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/finder/tree/v5.4.8"
+                "source": "https://github.com/symfony/finder/tree/v5.4.11"
             },
             "funding": [
                 {
@@ -2833,20 +2966,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-04-15T08:07:45+00:00"
+            "time": "2022-07-29T07:37:50+00:00"
         },
         {
             "name": "symfony/framework-bundle",
-            "version": "v5.4.9",
+            "version": "v5.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/framework-bundle.git",
-                "reference": "1cb89cd3e36d5060545d0f223f00a774fa6430ef"
+                "reference": "a0660b602357d5c2ceaac1c9f80c5820bbff803d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/1cb89cd3e36d5060545d0f223f00a774fa6430ef",
-                "reference": "1cb89cd3e36d5060545d0f223f00a774fa6430ef",
+                "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/a0660b602357d5c2ceaac1c9f80c5820bbff803d",
+                "reference": "a0660b602357d5c2ceaac1c9f80c5820bbff803d",
                 "shasum": ""
             },
             "require": {
@@ -2968,7 +3101,7 @@
             "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/framework-bundle/tree/v5.4.9"
+                "source": "https://github.com/symfony/framework-bundle/tree/v5.4.11"
             },
             "funding": [
                 {
@@ -2984,20 +3117,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-27T06:29:07+00:00"
+            "time": "2022-07-20T13:00:38+00:00"
         },
         {
             "name": "symfony/http-foundation",
-            "version": "v5.4.9",
+            "version": "v5.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-foundation.git",
-                "reference": "6b0d0e4aca38d57605dcd11e2416994b38774522"
+                "reference": "0a5868e0999e9d47859ba3d918548ff6943e6389"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6b0d0e4aca38d57605dcd11e2416994b38774522",
-                "reference": "6b0d0e4aca38d57605dcd11e2416994b38774522",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/0a5868e0999e9d47859ba3d918548ff6943e6389",
+                "reference": "0a5868e0999e9d47859ba3d918548ff6943e6389",
                 "shasum": ""
             },
             "require": {
@@ -3041,7 +3174,7 @@
             "description": "Defines an object-oriented layer for the HTTP specification",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/http-foundation/tree/v5.4.9"
+                "source": "https://github.com/symfony/http-foundation/tree/v5.4.11"
             },
             "funding": [
                 {
@@ -3057,20 +3190,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-17T15:07:29+00:00"
+            "time": "2022-07-20T13:00:38+00:00"
         },
         {
             "name": "symfony/http-kernel",
-            "version": "v5.4.9",
+            "version": "v5.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-kernel.git",
-                "reference": "34b121ad3dc761f35fe1346d2f15618f8cbf77f8"
+                "reference": "4fd590a2ef3f62560dbbf6cea511995dd77321ee"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/34b121ad3dc761f35fe1346d2f15618f8cbf77f8",
-                "reference": "34b121ad3dc761f35fe1346d2f15618f8cbf77f8",
+                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/4fd590a2ef3f62560dbbf6cea511995dd77321ee",
+                "reference": "4fd590a2ef3f62560dbbf6cea511995dd77321ee",
                 "shasum": ""
             },
             "require": {
@@ -3153,7 +3286,7 @@
             "description": "Provides a structured process for converting a Request into a Response",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/http-kernel/tree/v5.4.9"
+                "source": "https://github.com/symfony/http-kernel/tree/v5.4.11"
             },
             "funding": [
                 {
@@ -3169,20 +3302,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-27T07:09:08+00:00"
+            "time": "2022-07-29T12:30:22+00:00"
         },
         {
             "name": "symfony/intl",
-            "version": "v5.4.8",
+            "version": "v5.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/intl.git",
-                "reference": "b9e17d7ab867ce99f89950ebced0fa91076ba12b"
+                "reference": "d305c0c1d31b30b3876e041804c35e49e5f8a96e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/intl/zipball/b9e17d7ab867ce99f89950ebced0fa91076ba12b",
-                "reference": "b9e17d7ab867ce99f89950ebced0fa91076ba12b",
+                "url": "https://api.github.com/repos/symfony/intl/zipball/d305c0c1d31b30b3876e041804c35e49e5f8a96e",
+                "reference": "d305c0c1d31b30b3876e041804c35e49e5f8a96e",
                 "shasum": ""
             },
             "require": {
@@ -3241,7 +3374,7 @@
                 "localization"
             ],
             "support": {
-                "source": "https://github.com/symfony/intl/tree/v5.4.8"
+                "source": "https://github.com/symfony/intl/tree/v5.4.11"
             },
             "funding": [
                 {
@@ -3257,7 +3390,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-04-07T09:39:59+00:00"
+            "time": "2022-07-20T11:34:24+00:00"
         },
         {
             "name": "symfony/polyfill-ctype",
@@ -3832,16 +3965,16 @@
         },
         {
             "name": "symfony/process",
-            "version": "v5.4.8",
+            "version": "v5.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/process.git",
-                "reference": "597f3fff8e3e91836bb0bd38f5718b56ddbde2f3"
+                "reference": "6e75fe6874cbc7e4773d049616ab450eff537bf1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/process/zipball/597f3fff8e3e91836bb0bd38f5718b56ddbde2f3",
-                "reference": "597f3fff8e3e91836bb0bd38f5718b56ddbde2f3",
+                "url": "https://api.github.com/repos/symfony/process/zipball/6e75fe6874cbc7e4773d049616ab450eff537bf1",
+                "reference": "6e75fe6874cbc7e4773d049616ab450eff537bf1",
                 "shasum": ""
             },
             "require": {
@@ -3874,7 +4007,7 @@
             "description": "Executes commands in sub-processes",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/process/tree/v5.4.8"
+                "source": "https://github.com/symfony/process/tree/v5.4.11"
             },
             "funding": [
                 {
@@ -3890,20 +4023,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-04-08T05:07:18+00:00"
+            "time": "2022-06-27T16:58:25+00:00"
         },
         {
             "name": "symfony/routing",
-            "version": "v5.4.8",
+            "version": "v5.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/routing.git",
-                "reference": "e07817bb6244ea33ef5ad31abc4a9288bef3f2f7"
+                "reference": "3e01ccd9b2a3a4167ba2b3c53612762300300226"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/routing/zipball/e07817bb6244ea33ef5ad31abc4a9288bef3f2f7",
-                "reference": "e07817bb6244ea33ef5ad31abc4a9288bef3f2f7",
+                "url": "https://api.github.com/repos/symfony/routing/zipball/3e01ccd9b2a3a4167ba2b3c53612762300300226",
+                "reference": "3e01ccd9b2a3a4167ba2b3c53612762300300226",
                 "shasum": ""
             },
             "require": {
@@ -3964,7 +4097,7 @@
                 "url"
             ],
             "support": {
-                "source": "https://github.com/symfony/routing/tree/v5.4.8"
+                "source": "https://github.com/symfony/routing/tree/v5.4.11"
             },
             "funding": [
                 {
@@ -3980,20 +4113,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-04-18T21:45:37+00:00"
+            "time": "2022-07-20T13:00:38+00:00"
         },
         {
             "name": "symfony/service-contracts",
-            "version": "v2.5.1",
+            "version": "v2.5.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/service-contracts.git",
-                "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c"
+                "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/24d9dc654b83e91aa59f9d167b131bc3b5bea24c",
-                "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c",
+                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c",
+                "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c",
                 "shasum": ""
             },
             "require": {
@@ -4047,7 +4180,7 @@
                 "standards"
             ],
             "support": {
-                "source": "https://github.com/symfony/service-contracts/tree/v2.5.1"
+                "source": "https://github.com/symfony/service-contracts/tree/v2.5.2"
             },
             "funding": [
                 {
@@ -4063,20 +4196,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-03-13T20:07:29+00:00"
+            "time": "2022-05-30T19:17:29+00:00"
         },
         {
             "name": "symfony/string",
-            "version": "v5.4.9",
+            "version": "v5.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/string.git",
-                "reference": "985e6a9703ef5ce32ba617c9c7d97873bb7b2a99"
+                "reference": "5eb661e49ad389e4ae2b6e4df8d783a8a6548322"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/string/zipball/985e6a9703ef5ce32ba617c9c7d97873bb7b2a99",
-                "reference": "985e6a9703ef5ce32ba617c9c7d97873bb7b2a99",
+                "url": "https://api.github.com/repos/symfony/string/zipball/5eb661e49ad389e4ae2b6e4df8d783a8a6548322",
+                "reference": "5eb661e49ad389e4ae2b6e4df8d783a8a6548322",
                 "shasum": ""
             },
             "require": {
@@ -4133,7 +4266,7 @@
                 "utf8"
             ],
             "support": {
-                "source": "https://github.com/symfony/string/tree/v5.4.9"
+                "source": "https://github.com/symfony/string/tree/v5.4.11"
             },
             "funding": [
                 {
@@ -4149,20 +4282,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-04-19T10:40:37+00:00"
+            "time": "2022-07-24T16:15:25+00:00"
         },
         {
             "name": "symfony/translation-contracts",
-            "version": "v2.5.1",
+            "version": "v2.5.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation-contracts.git",
-                "reference": "1211df0afa701e45a04253110e959d4af4ef0f07"
+                "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/1211df0afa701e45a04253110e959d4af4ef0f07",
-                "reference": "1211df0afa701e45a04253110e959d4af4ef0f07",
+                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/136b19dd05cdf0709db6537d058bcab6dd6e2dbe",
+                "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe",
                 "shasum": ""
             },
             "require": {
@@ -4211,7 +4344,7 @@
                 "standards"
             ],
             "support": {
-                "source": "https://github.com/symfony/translation-contracts/tree/v2.5.1"
+                "source": "https://github.com/symfony/translation-contracts/tree/v2.5.2"
             },
             "funding": [
                 {
@@ -4227,20 +4360,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-01-02T09:53:40+00:00"
+            "time": "2022-06-27T16:58:25+00:00"
         },
         {
             "name": "symfony/twig-bridge",
-            "version": "v5.4.9",
+            "version": "v5.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/twig-bridge.git",
-                "reference": "fd13c89a1abdbaa7ee2e655d9a11405adcb7a6cf"
+                "reference": "63b8a50d48c9fe3d04e77307d4f1771dd848baa8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/fd13c89a1abdbaa7ee2e655d9a11405adcb7a6cf",
-                "reference": "fd13c89a1abdbaa7ee2e655d9a11405adcb7a6cf",
+                "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/63b8a50d48c9fe3d04e77307d4f1771dd848baa8",
+                "reference": "63b8a50d48c9fe3d04e77307d4f1771dd848baa8",
                 "shasum": ""
             },
             "require": {
@@ -4332,7 +4465,7 @@
             "description": "Provides integration for Twig with various Symfony components",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/twig-bridge/tree/v5.4.9"
+                "source": "https://github.com/symfony/twig-bridge/tree/v5.4.11"
             },
             "funding": [
                 {
@@ -4348,20 +4481,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-21T10:24:18+00:00"
+            "time": "2022-07-20T13:00:38+00:00"
         },
         {
             "name": "symfony/var-dumper",
-            "version": "v5.4.9",
+            "version": "v5.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-dumper.git",
-                "reference": "af52239a330fafd192c773795520dc2dd62b5657"
+                "reference": "b8f306d7b8ef34fb3db3305be97ba8e088fb4861"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/af52239a330fafd192c773795520dc2dd62b5657",
-                "reference": "af52239a330fafd192c773795520dc2dd62b5657",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/b8f306d7b8ef34fb3db3305be97ba8e088fb4861",
+                "reference": "b8f306d7b8ef34fb3db3305be97ba8e088fb4861",
                 "shasum": ""
             },
             "require": {
@@ -4421,7 +4554,7 @@
                 "dump"
             ],
             "support": {
-                "source": "https://github.com/symfony/var-dumper/tree/v5.4.9"
+                "source": "https://github.com/symfony/var-dumper/tree/v5.4.11"
             },
             "funding": [
                 {
@@ -4437,20 +4570,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-21T10:24:18+00:00"
+            "time": "2022-07-20T13:00:38+00:00"
         },
         {
             "name": "symfony/var-exporter",
-            "version": "v5.4.9",
+            "version": "v5.4.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-exporter.git",
-                "reference": "63249ebfca4e75a357679fa7ba2089cfb898aa67"
+                "reference": "8fc03ee75eeece3d9be1ef47d26d79bea1afb340"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/var-exporter/zipball/63249ebfca4e75a357679fa7ba2089cfb898aa67",
-                "reference": "63249ebfca4e75a357679fa7ba2089cfb898aa67",
+                "url": "https://api.github.com/repos/symfony/var-exporter/zipball/8fc03ee75eeece3d9be1ef47d26d79bea1afb340",
+                "reference": "8fc03ee75eeece3d9be1ef47d26d79bea1afb340",
                 "shasum": ""
             },
             "require": {
@@ -4494,7 +4627,7 @@
                 "serialize"
             ],
             "support": {
-                "source": "https://github.com/symfony/var-exporter/tree/v5.4.9"
+                "source": "https://github.com/symfony/var-exporter/tree/v5.4.10"
             },
             "funding": [
                 {
@@ -4510,20 +4643,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-21T10:24:18+00:00"
+            "time": "2022-05-27T12:56:18+00:00"
         },
         {
             "name": "symfony/yaml",
-            "version": "v5.4.3",
+            "version": "v5.4.11",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/yaml.git",
-                "reference": "e80f87d2c9495966768310fc531b487ce64237a2"
+                "reference": "05d4ea560f3402c6c116afd99fdc66e60eda227e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/yaml/zipball/e80f87d2c9495966768310fc531b487ce64237a2",
-                "reference": "e80f87d2c9495966768310fc531b487ce64237a2",
+                "url": "https://api.github.com/repos/symfony/yaml/zipball/05d4ea560f3402c6c116afd99fdc66e60eda227e",
+                "reference": "05d4ea560f3402c6c116afd99fdc66e60eda227e",
                 "shasum": ""
             },
             "require": {
@@ -4569,7 +4702,7 @@
             "description": "Loads and dumps YAML files",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/yaml/tree/v5.4.3"
+                "source": "https://github.com/symfony/yaml/tree/v5.4.11"
             },
             "funding": [
                 {
@@ -4585,20 +4718,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-01-26T16:32:32+00:00"
+            "time": "2022-06-27T16:58:25+00:00"
         },
         {
             "name": "twig/intl-extra",
-            "version": "v3.4.0",
+            "version": "v3.4.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/twigphp/intl-extra.git",
-                "reference": "8dca6f4c5a00cdd3c43b6bd080f50d32aca33a84"
+                "reference": "151e50fad9c7915bd56f0adf3f0cb3c47e6ed28a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/8dca6f4c5a00cdd3c43b6bd080f50d32aca33a84",
-                "reference": "8dca6f4c5a00cdd3c43b6bd080f50d32aca33a84",
+                "url": "https://api.github.com/repos/twigphp/intl-extra/zipball/151e50fad9c7915bd56f0adf3f0cb3c47e6ed28a",
+                "reference": "151e50fad9c7915bd56f0adf3f0cb3c47e6ed28a",
                 "shasum": ""
             },
             "require": {
@@ -4642,7 +4775,7 @@
                 "twig"
             ],
             "support": {
-                "source": "https://github.com/twigphp/intl-extra/tree/v3.4.0"
+                "source": "https://github.com/twigphp/intl-extra/tree/v3.4.2"
             },
             "funding": [
                 {
@@ -4654,20 +4787,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-01-02T10:02:25+00:00"
+            "time": "2022-06-10T08:33:05+00:00"
         },
         {
             "name": "twig/twig",
-            "version": "v3.4.1",
+            "version": "v3.4.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/twigphp/Twig.git",
-                "reference": "e939eae92386b69b49cfa4599dd9bead6bf4a342"
+                "reference": "e07cdd3d430cd7e453c31b36eb5ad6c0c5e43077"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/twigphp/Twig/zipball/e939eae92386b69b49cfa4599dd9bead6bf4a342",
-                "reference": "e939eae92386b69b49cfa4599dd9bead6bf4a342",
+                "url": "https://api.github.com/repos/twigphp/Twig/zipball/e07cdd3d430cd7e453c31b36eb5ad6c0c5e43077",
+                "reference": "e07cdd3d430cd7e453c31b36eb5ad6c0c5e43077",
                 "shasum": ""
             },
             "require": {
@@ -4718,7 +4851,7 @@
             ],
             "support": {
                 "issues": "https://github.com/twigphp/Twig/issues",
-                "source": "https://github.com/twigphp/Twig/tree/v3.4.1"
+                "source": "https://github.com/twigphp/Twig/tree/v3.4.2"
             },
             "funding": [
                 {
@@ -4730,7 +4863,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-05-17T05:48:52+00:00"
+            "time": "2022-08-12T06:47:24+00:00"
         },
         {
             "name": "webmozart/assert",
@@ -6116,16 +6249,16 @@
         },
         {
             "name": "phpunit/phpunit",
-            "version": "9.5.20",
+            "version": "9.5.21",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba"
+                "reference": "0e32b76be457de00e83213528f6bb37e2a38fcb1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba",
-                "reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0e32b76be457de00e83213528f6bb37e2a38fcb1",
+                "reference": "0e32b76be457de00e83213528f6bb37e2a38fcb1",
                 "shasum": ""
             },
             "require": {
@@ -6159,7 +6292,6 @@
                 "sebastian/version": "^3.0.2"
             },
             "require-dev": {
-                "ext-pdo": "*",
                 "phpspec/prophecy-phpunit": "^2.0.1"
             },
             "suggest": {
@@ -6203,7 +6335,7 @@
             ],
             "support": {
                 "issues": "https://github.com/sebastianbergmann/phpunit/issues",
-                "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20"
+                "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.21"
             },
             "funding": [
                 {
@@ -6215,7 +6347,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2022-04-01T12:37:26+00:00"
+            "time": "2022-06-19T12:14:25+00:00"
         },
         {
             "name": "sebastian/cli-parser",
@@ -7240,16 +7372,16 @@
         },
         {
             "name": "squizlabs/php_codesniffer",
-            "version": "3.7.0",
+            "version": "3.7.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
-                "reference": "a2cd51b45bcaef9c1f2a4bda48f2dd2fa2b95563"
+                "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/a2cd51b45bcaef9c1f2a4bda48f2dd2fa2b95563",
-                "reference": "a2cd51b45bcaef9c1f2a4bda48f2dd2fa2b95563",
+                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/1359e176e9307e906dc3d890bcc9603ff6d90619",
+                "reference": "1359e176e9307e906dc3d890bcc9603ff6d90619",
                 "shasum": ""
             },
             "require": {
@@ -7292,20 +7424,20 @@
                 "source": "https://github.com/squizlabs/PHP_CodeSniffer",
                 "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
             },
-            "time": "2022-06-13T06:31:38+00:00"
+            "time": "2022-06-18T07:21:10+00:00"
         },
         {
             "name": "symfony/phpunit-bridge",
-            "version": "v6.1.0",
+            "version": "v6.1.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/phpunit-bridge.git",
-                "reference": "092ccc3b364925cd8ed6046bc31dcf3a022bd5a4"
+                "reference": "75c2fa71d049c1f48e39d208c0cefba97e66335a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/092ccc3b364925cd8ed6046bc31dcf3a022bd5a4",
-                "reference": "092ccc3b364925cd8ed6046bc31dcf3a022bd5a4",
+                "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/75c2fa71d049c1f48e39d208c0cefba97e66335a",
+                "reference": "75c2fa71d049c1f48e39d208c0cefba97e66335a",
                 "shasum": ""
             },
             "require": {
@@ -7359,7 +7491,7 @@
             "description": "Provides utilities for PHPUnit, especially user deprecation notices management",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/phpunit-bridge/tree/v6.1.0"
+                "source": "https://github.com/symfony/phpunit-bridge/tree/v6.1.3"
             },
             "funding": [
                 {
@@ -7375,7 +7507,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2022-04-12T16:22:53+00:00"
+            "time": "2022-07-28T13:40:41+00:00"
         },
         {
             "name": "theseer/tokenizer",
@@ -7429,16 +7561,16 @@
         },
         {
             "name": "vimeo/psalm",
-            "version": "4.23.0",
+            "version": "4.26.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/vimeo/psalm.git",
-                "reference": "f1fe6ff483bf325c803df9f510d09a03fd796f88"
+                "reference": "6998fabb2bf528b65777bf9941920888d23c03ac"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/vimeo/psalm/zipball/f1fe6ff483bf325c803df9f510d09a03fd796f88",
-                "reference": "f1fe6ff483bf325c803df9f510d09a03fd796f88",
+                "url": "https://api.github.com/repos/vimeo/psalm/zipball/6998fabb2bf528b65777bf9941920888d23c03ac",
+                "reference": "6998fabb2bf528b65777bf9941920888d23c03ac",
                 "shasum": ""
             },
             "require": {
@@ -7530,9 +7662,9 @@
             ],
             "support": {
                 "issues": "https://github.com/vimeo/psalm/issues",
-                "source": "https://github.com/vimeo/psalm/tree/4.23.0"
+                "source": "https://github.com/vimeo/psalm/tree/4.26.0"
             },
-            "time": "2022-04-28T17:35:49+00:00"
+            "time": "2022-07-31T13:10:26+00:00"
         },
         {
             "name": "webmozart/path-util",
diff --git a/src/Entities/AuthenticationEvent/Job.php b/src/Entities/AuthenticationEvent/Job.php
index 51308d6fbba4657e453e3b7b4d80240897d91036..eaa66aba53624337e61f73e3417e25f7e0bac2fa 100644
--- a/src/Entities/AuthenticationEvent/Job.php
+++ b/src/Entities/AuthenticationEvent/Job.php
@@ -5,11 +5,11 @@ declare(strict_types=1);
 namespace SimpleSAML\Module\accounting\Entities\AuthenticationEvent;
 
 use SimpleSAML\Module\accounting\Entities\AuthenticationEvent;
+use SimpleSAML\Module\accounting\Entities\Bases\AbstractJob;
 use SimpleSAML\Module\accounting\Entities\Bases\AbstractPayload;
-use SimpleSAML\Module\accounting\Entities\GenericJob;
 use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
 
-class Job extends GenericJob
+class Job extends AbstractJob
 {
     public function getPayload(): AuthenticationEvent
     {
@@ -24,9 +24,14 @@ class Job extends GenericJob
     protected function validatePayload(AbstractPayload $payload): AuthenticationEvent
     {
         if (! ($payload instanceof AuthenticationEvent)) {
-            throw new UnexpectedValueException('Job payload must be of type AuthenticationEvent.');
+            throw new UnexpectedValueException('AuthenticationEvent Job payload must be of type AuthenticationEvent.');
         }
 
         return $payload;
     }
+
+    public function getType(): string
+    {
+        return self::class;
+    }
 }
diff --git a/src/Entities/Bases/AbstractJob.php b/src/Entities/Bases/AbstractJob.php
index b2f1f73968f4d3adcf622f5e06406e6e230aae25..a41f3450c2dde05e438296ccd487cc83fd710e12 100644
--- a/src/Entities/Bases/AbstractJob.php
+++ b/src/Entities/Bases/AbstractJob.php
@@ -9,10 +9,22 @@ use SimpleSAML\Module\accounting\Entities\Interfaces\JobInterface;
 abstract class AbstractJob implements JobInterface
 {
     protected AbstractPayload $payload;
+    protected ?int $id;
+    protected \DateTimeImmutable $createdAt;
 
-    public function __construct(AbstractPayload $payload)
-    {
+    public function __construct(
+        AbstractPayload $payload,
+        int $id = null,
+        \DateTimeImmutable $createdAt = null
+    ) {
         $this->setPayload($payload);
+        $this->id = $id;
+        $this->createdAt = $createdAt ?? new \DateTimeImmutable();
+    }
+
+    public function getId(): ?int
+    {
+        return $this->id;
     }
 
     public function getPayload(): AbstractPayload
@@ -24,4 +36,11 @@ abstract class AbstractJob implements JobInterface
     {
         $this->payload = $payload;
     }
+
+    public function getCreatedAt(): \DateTimeImmutable
+    {
+        return $this->createdAt;
+    }
+
+    abstract public function getType(): string;
 }
diff --git a/src/Entities/GenericJob.php b/src/Entities/GenericJob.php
index 03aa70051f005bde1e63e11b062d61c9449702f8..6577929bb3a978eae43619ace4a6aa1a69fb14bb 100644
--- a/src/Entities/GenericJob.php
+++ b/src/Entities/GenericJob.php
@@ -4,4 +4,8 @@ namespace SimpleSAML\Module\accounting\Entities;
 
 class GenericJob extends Bases\AbstractJob
 {
+    public function getType(): string
+    {
+        return self::class;
+    }
 }
diff --git a/src/Entities/Interfaces/JobInterface.php b/src/Entities/Interfaces/JobInterface.php
index c4622c3b09ced9fa1547a945759d6f9bfb7aba6a..7ebc01969c8676a6e51cad6aba7f87dc71d47e03 100644
--- a/src/Entities/Interfaces/JobInterface.php
+++ b/src/Entities/Interfaces/JobInterface.php
@@ -8,7 +8,12 @@ use SimpleSAML\Module\accounting\Entities\Bases\AbstractPayload;
 
 interface JobInterface
 {
-    public function getPayload(): AbstractPayload;
+    public function getId(): ?int;
 
+    public function getPayload(): AbstractPayload;
     public function setPayload(AbstractPayload $payload): void;
+
+    public function getType(): string;
+
+    public function getCreatedAt(): \DateTimeImmutable;
 }
diff --git a/src/Stores/Interfaces/JobsStoreInterface.php b/src/Stores/Interfaces/JobsStoreInterface.php
index 52d973bb7f08d768cc9650553059148e63f7010f..0ea21d761bcea526fd3e73859b28e1725284cdd3 100644
--- a/src/Stores/Interfaces/JobsStoreInterface.php
+++ b/src/Stores/Interfaces/JobsStoreInterface.php
@@ -11,10 +11,9 @@ interface JobsStoreInterface extends StoreInterface
     /**
      * Add job to queue
      * @param JobInterface $job
-     * @param string|null $type Optional job type designation. If not null, payload class FQN will be used.
      * @return void
      */
-    public function enqueue(JobInterface $job, string $type = null): void;
+    public function enqueue(JobInterface $job): void;
 
     /**
      * Get job from queue
diff --git a/src/Stores/Jobs/DoctrineDbal/JobsStore.php b/src/Stores/Jobs/DoctrineDbal/JobsStore.php
index 30f760d5bf4f352a6925c02a5d4881276f262dcf..5507ab339391a7b488eb5328f6303c01f9884e43 100644
--- a/src/Stores/Jobs/DoctrineDbal/JobsStore.php
+++ b/src/Stores/Jobs/DoctrineDbal/JobsStore.php
@@ -4,21 +4,19 @@ declare(strict_types=1);
 
 namespace SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal;
 
-use DateTimeImmutable;
-use Doctrine\DBAL\Result;
-use Doctrine\DBAL\Types\Types;
 use Psr\Log\LoggerInterface;
 use ReflectionClass;
-use SimpleSAML\Module\accounting\Entities\GenericJob;
 use SimpleSAML\Module\accounting\Entities\Interfaces\JobInterface;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\Exceptions\StoreException\MigrationException;
+use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator;
 use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
 use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory;
 use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator;
 use SimpleSAML\Module\accounting\Stores\Interfaces\JobsStoreInterface;
+use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore\Repository;
 use Throwable;
 
 class JobsStore implements JobsStoreInterface
@@ -39,11 +37,13 @@ class JobsStore implements JobsStoreInterface
     protected string $prefixedTableNameFailedJobs;
     protected Migrator $migrator;
     protected LoggerInterface $logger;
+    protected Repository $jobsRepository;
 
     public function __construct(
         ModuleConfiguration $moduleConfiguration,
         Factory $factory,
-        LoggerInterface $logger
+        LoggerInterface $logger,
+        Repository $jobsRepository = null
     ) {
         $this->moduleConfiguration = $moduleConfiguration;
         $this->connection = $factory->buildConnection($moduleConfiguration->getStoreConnection(self::class));
@@ -52,59 +52,17 @@ class JobsStore implements JobsStoreInterface
 
         $this->prefixedTableNameJobs = $this->connection->preparePrefixedTableName(self::TABLE_NAME_JOBS);
         $this->prefixedTableNameFailedJobs = $this->connection->preparePrefixedTableName(self::TABLE_NAME_FAILED_JOBS);
-    }
-
-    /**
-     * @throws StoreException
-     */
-    public function enqueue(JobInterface $job, string $type = null): void
-    {
-        $queryBuilder = $this->connection->dbal()->createQueryBuilder();
-
-        $payload = $job->getPayload();
-        $type = $this->validateType($type ?? get_class($job));
 
-        $queryBuilder->insert($this->prefixedTableNameJobs)
-            ->values(
-                [
-                    self::COLUMN_NAME_PAYLOAD => ':' . self::COLUMN_NAME_PAYLOAD,
-                    self::COLUMN_NAME_TYPE => ':' . self::COLUMN_NAME_TYPE,
-                    self::COLUMN_NAME_CREATED_AT => ':' . self::COLUMN_NAME_CREATED_AT,
-                ]
-            )
-            ->setParameters(
-                [
-                    self::COLUMN_NAME_PAYLOAD => serialize($payload),
-                    self::COLUMN_NAME_TYPE => $type,
-                    self::COLUMN_NAME_CREATED_AT => new DateTimeImmutable(),
-                ],
-                [
-                    self::COLUMN_NAME_PAYLOAD => Types::TEXT,
-                    self::COLUMN_NAME_TYPE => Types::STRING,
-                    self::COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE
-                ]
-            );
-
-        try {
-            $queryBuilder->executeStatement();
-        } catch (Throwable $exception) {
-            $message = sprintf('Could not enqueue job (%s)', $exception->getMessage());
-            throw new StoreException($message, (int)$exception->getCode(), $exception);
-        }
+        $this->jobsRepository = $jobsRepository ??
+            new Repository($this->connection, $this->prefixedTableNameJobs, $this->logger);
     }
 
     /**
      * @throws StoreException
      */
-    protected function validateType(string $type): string
+    public function enqueue(JobInterface $job): void
     {
-        if (mb_strlen($type) > self::COLUMN_LENGTH_TYPE) {
-            throw new StoreException(
-                sprintf('String length for type column exceeds %s limit.', self::COLUMN_LENGTH_TYPE)
-            );
-        }
-
-        return $type;
+        $this->jobsRepository->insert($job);
     }
 
     /**
@@ -112,28 +70,35 @@ class JobsStore implements JobsStoreInterface
      */
     public function dequeue(string $type = null): ?JobInterface
     {
+        /** @noinspection PhpUnusedLocalVariableInspection - psalm reports possibly undefined variable */
         $job = null;
+        $attempts = 0;
+        $maxDeleteAttempts = 3;
 
         try {
             // Check if there are any jobs in the store...
-            while ($row = $this->getNext($type)->fetchAssociative()) {
-                // We have a row. Let's create a row job instance, which will take care of validation and make it easier
-                // to work with instead of array.
-                $rawJob = new RawJob($row, $this->connection->dbal()->getDatabasePlatform());
+            while (($job = $this->jobsRepository->getNext($type)) !== null) {
+                // We have job instance.
+                $jobId = $job->getId();
+
+                if ($jobId === null) {
+                    throw new UnexpectedValueException('Retrieved job does not contain ID.');
+                }
+
+                $attempts++;
+
                 // Let's try to delete this job from the store, so it can't be fetched again.
-                $numberOfAffectedRows = $this->delete($rawJob->getId());
-                if ($numberOfAffectedRows === 0) {
-                    // It seems that this job has already been dequeued in the meantime. Try to get next job again.
+                if ($this->jobsRepository->delete($jobId) === false) {
+                    // It seems that this job has already been deleted in the meantime.
+                    // Check if this happened before. If threshold is reached, throw.
+                    // Otherwise, try to get next job again.
+                    if ($attempts > $maxDeleteAttempts) {
+                        $message = 'Job retrieval was successful, however it was deleted in the meantime.';
+                        throw new StoreException($message);
+                    }
+
                     continue;
                 }
-                // Job is deleted, meaning it is now dequeued and we can return a new job instance.
-                // If a valid type is declared, let's try to instantiate a job of that specific type.
-                if ($type !== null && class_exists($type) && is_subclass_of($type, JobInterface::class)) {
-                    $job = (new ReflectionClass($type))->newInstance($rawJob->getPayload());
-                } else {
-                    // No (valid) job type, so generic job will do...
-                    $job = new GenericJob($rawJob->getPayload());
-                }
 
                 // We have found and dequeued a job, so finish with the search.
                 break;
@@ -141,7 +106,7 @@ class JobsStore implements JobsStoreInterface
         } catch (Throwable $exception) {
             throw new StoreException(
                 'Error while trying to dequeue a job.',
-                (int) $exception->getCode(),
+                (int)$exception->getCode(),
                 $exception
             );
         }
@@ -149,50 +114,6 @@ class JobsStore implements JobsStoreInterface
         return $job;
     }
 
-    /**
-     * @throws StoreException
-     */
-    public function getNext(string $type = null): Result
-    {
-        $queryBuilder = $this->connection->dbal()->createQueryBuilder();
-
-        /**
-         * @psalm-suppress TooManyArguments - providing array or null is deprecated
-         */
-        $queryBuilder->select(
-            self::COLUMN_NAME_ID,
-            self::COLUMN_NAME_PAYLOAD,
-            self::COLUMN_NAME_TYPE,
-            self::COLUMN_NAME_CREATED_AT
-        )
-            ->from($this->prefixedTableNameJobs)
-            ->orderBy(self::COLUMN_NAME_ID)
-            ->setMaxResults(1);
-
-        if ($type !== null) {
-            $queryBuilder->where(self::COLUMN_NAME_TYPE . ' = ' . $queryBuilder->createNamedParameter($type));
-        }
-
-        try {
-            $result = $queryBuilder->executeQuery();
-        } catch (Throwable $exception) {
-            $message = 'Error while trying to execute query to get next available job.';
-            throw new StoreException($message, (int)$exception->getCode(), $exception);
-        }
-
-        return $result;
-    }
-
-    protected function delete(int $id): int
-    {
-        return (int)$this->connection->dbal()
-            ->delete(
-                $this->prefixedTableNameJobs,
-                [self::COLUMN_NAME_ID => $id],
-                [self::COLUMN_NAME_ID => Types::BIGINT]
-            );
-    }
-
     /**
      * @throws StoreException
      * @throws MigrationException
diff --git a/src/Stores/Jobs/DoctrineDbal/RawJob.php b/src/Stores/Jobs/DoctrineDbal/JobsStore/RawJob.php
similarity index 96%
rename from src/Stores/Jobs/DoctrineDbal/RawJob.php
rename to src/Stores/Jobs/DoctrineDbal/JobsStore/RawJob.php
index e66eb263925311c1331a20894cf8e331d6f18542..f22a73f66182fd9d20df945ac527a9c01104d145 100644
--- a/src/Stores/Jobs/DoctrineDbal/RawJob.php
+++ b/src/Stores/Jobs/DoctrineDbal/JobsStore/RawJob.php
@@ -2,13 +2,14 @@
 
 declare(strict_types=1);
 
-namespace SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal;
+namespace SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore;
 
 use Doctrine\DBAL\Platforms\AbstractPlatform;
 use Doctrine\DBAL\Types\Type;
 use Doctrine\DBAL\Types\Types;
 use SimpleSAML\Module\accounting\Entities\Bases\AbstractPayload;
 use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
+use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore;
 
 class RawJob
 {
diff --git a/src/Stores/Jobs/DoctrineDbal/JobsStore/Repository.php b/src/Stores/Jobs/DoctrineDbal/JobsStore/Repository.php
new file mode 100644
index 0000000000000000000000000000000000000000..2c06c20276f208a18e250cf63404944f8ad59f80
--- /dev/null
+++ b/src/Stores/Jobs/DoctrineDbal/JobsStore/Repository.php
@@ -0,0 +1,190 @@
+<?php
+
+declare(strict_types=1);
+
+namespace SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore;
+
+use Doctrine\DBAL\Types\Types;
+use Psr\Log\LoggerInterface;
+use ReflectionClass;
+use SimpleSAML\Module\accounting\Entities\GenericJob;
+use SimpleSAML\Module\accounting\Entities\Interfaces\JobInterface;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\Services\LoggerService;
+use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore;
+use Throwable;
+
+class Repository
+{
+    protected Connection $connection;
+
+    protected array $validJobsTableNames = [];
+
+    protected string $tableName;
+    protected LoggerInterface $logger;
+
+    public function __construct(Connection $connection, string $tableName, LoggerInterface $logger)
+    {
+        $this->connection = $connection;
+
+        $this->prepareValidJobsTableNames();
+
+        $this->validateTableName($tableName);
+
+        $this->tableName = $tableName;
+        $this->logger = $logger;
+    }
+
+    protected function prepareValidJobsTableNames(): void
+    {
+        $this->validJobsTableNames[] = $this->connection
+            ->preparePrefixedTableName(JobsStore::TABLE_NAME_JOBS);
+        $this->validJobsTableNames[] = $this->connection
+            ->preparePrefixedTableName(JobsStore::TABLE_NAME_FAILED_JOBS);
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function insert(JobInterface $job): void
+    {
+        $this->validateType($job->getType());
+
+        $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+        $queryBuilder->insert($this->tableName)
+            ->values(
+                [
+                    JobsStore::COLUMN_NAME_PAYLOAD => ':' . JobsStore::COLUMN_NAME_PAYLOAD,
+                    JobsStore::COLUMN_NAME_TYPE => ':' . JobsStore::COLUMN_NAME_TYPE,
+                    JobsStore::COLUMN_NAME_CREATED_AT => ':' . JobsStore::COLUMN_NAME_CREATED_AT,
+                ]
+            )
+            ->setParameters(
+                [
+                    JobsStore::COLUMN_NAME_PAYLOAD => serialize($job->getPayload()),
+                    JobsStore::COLUMN_NAME_TYPE => $job->getType(),
+                    JobsStore::COLUMN_NAME_CREATED_AT => $job->getCreatedAt(),
+                ],
+                [
+                    JobsStore::COLUMN_NAME_PAYLOAD => Types::TEXT,
+                    JobsStore::COLUMN_NAME_TYPE => Types::STRING,
+                    JobsStore::COLUMN_NAME_CREATED_AT => Types::DATETIMETZ_IMMUTABLE
+                ]
+            );
+
+        try {
+            $queryBuilder->executeStatement();
+        } catch (Throwable $exception) {
+            $message = sprintf('Could not insert job (%s)', $exception->getMessage());
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+    }
+
+    /**
+     * @param string|null $type
+     * @return ?JobInterface
+     * @throws StoreException
+     */
+    public function getNext(string $type = null): ?JobInterface
+    {
+        $queryBuilder = $this->connection->dbal()->createQueryBuilder();
+
+        /**
+         * @psalm-suppress TooManyArguments - providing array or null is deprecated
+         */
+        $queryBuilder->select(
+            JobsStore::COLUMN_NAME_ID,
+            JobsStore::COLUMN_NAME_PAYLOAD,
+            JobsStore::COLUMN_NAME_TYPE,
+            JobsStore::COLUMN_NAME_CREATED_AT
+        )
+            ->from($this->tableName)
+            ->orderBy(JobsStore::COLUMN_NAME_ID)
+            ->setMaxResults(1);
+
+        if ($type !== null) {
+            $queryBuilder->where(JobsStore::COLUMN_NAME_TYPE . ' = ' . $queryBuilder->createNamedParameter($type));
+        }
+
+        try {
+            $result = $queryBuilder->executeQuery();
+            $row = $result->fetchAssociative();
+        } catch (Throwable $exception) {
+            $message = 'Error while trying to execute query to get next available job.';
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+
+        if ($row === false) {
+            return null;
+        }
+
+        try {
+            $rawJob = new RawJob($row, $this->connection->dbal()->getDatabasePlatform());
+            $rawJobType = $rawJob->getType();
+
+            // Try to create a specific job type. Otherwise, create a generic one.
+            if (class_exists($rawJobType) && is_subclass_of($rawJobType, JobInterface::class)) {
+                $job = (new ReflectionClass($rawJobType))
+                    ->newInstance($rawJob->getPayload(), $rawJob->getId(), $rawJob->getCreatedAt());
+            } else {
+                // No (valid) job type, so generic one will do...
+                $job = new GenericJob($rawJob->getPayload(), $rawJob->getId(), $rawJob->getCreatedAt());
+            }
+        } catch (Throwable $exception) {
+            $message = 'Could not create a job instance.';
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+
+        return $job;
+    }
+
+    /**
+     * @throws StoreException
+     */
+    public function delete(int $id): bool
+    {
+        try {
+            $numberOfAffectedRows = (int)$this->connection->dbal()
+                ->delete(
+                    $this->tableName,
+                    [JobsStore::COLUMN_NAME_ID => $id],
+                    [JobsStore::COLUMN_NAME_ID => Types::BIGINT]
+                );
+        } catch (Throwable $exception) {
+            $message = sprintf('Error while trying to delete a job with ID %s.', $id);
+            throw new StoreException($message, (int)$exception->getCode(), $exception);
+        }
+
+        if ($numberOfAffectedRows === 0) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * @throws StoreException
+     */
+    protected function validateTableName(string $tableName): void
+    {
+        if (!in_array($tableName, $this->validJobsTableNames)) {
+            throw new StoreException(
+                sprintf('Table %s is not valid table for storing jobs.', $tableName)
+            );
+        }
+    }
+
+    /**
+     * @throws StoreException
+     */
+    protected function validateType(string $type): void
+    {
+        if (mb_strlen($type) > JobsStore::COLUMN_LENGTH_TYPE) {
+            throw new StoreException(
+                sprintf('String length for type column exceeds %s limit.', JobsStore::COLUMN_LENGTH_TYPE)
+            );
+        }
+    }
+}
diff --git a/tests/src/Entities/AuthenticationEvent/JobTest.php b/tests/src/Entities/AuthenticationEvent/JobTest.php
index 16824118e8d757a34b08f6e4c8502fa52e12e82f..cbe5e8bf5d3da071d54d62a1af4d3ed2abeeb22a 100644
--- a/tests/src/Entities/AuthenticationEvent/JobTest.php
+++ b/tests/src/Entities/AuthenticationEvent/JobTest.php
@@ -31,4 +31,11 @@ class JobTest extends TestCase
 
         (new Job($payload));
     }
+
+    public function testCanGetProperType(): void
+    {
+        $job = new Job(new AuthenticationEvent(['sample' => 'state']));
+
+        $this->assertSame(Job::class, $job->getType());
+    }
 }
diff --git a/tests/src/Entities/Bases/AbstractJobTest.php b/tests/src/Entities/Bases/AbstractJobTest.php
index 86fa21be640475adb53764e39f71e74f8c8f6cf0..88d265716e59a622069ab65aa2ef27488b51a7b9 100644
--- a/tests/src/Entities/Bases/AbstractJobTest.php
+++ b/tests/src/Entities/Bases/AbstractJobTest.php
@@ -23,14 +23,20 @@ class AbstractJobTest extends TestCase
         };
     }
 
-    public function testCanSetAndGetPayload(): void
+    public function testCanInitializeProperties(): void
     {
-        $job = new class ($this->payload) extends AbstractJob  {
-            public function run(): void
+        $id = 1;
+        $createdAt = new \DateTimeImmutable();
+        $job = new class ($this->payload, $id, $createdAt) extends AbstractJob  {
+            public function getType(): string
             {
+                return self::class;
             }
         };
 
+        $this->assertSame($id, $job->getId());
+        $this->assertSame($createdAt, $job->getCreatedAt());
+        $this->assertSame(get_class($job), $job->getType());
         $this->assertInstanceOf(AbstractPayload::class, $job->getPayload());
     }
 }
diff --git a/tests/src/Entities/GenericJobTest.php b/tests/src/Entities/GenericJobTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..11aed48d0183f49e9204d63d734caa07a193322a
--- /dev/null
+++ b/tests/src/Entities/GenericJobTest.php
@@ -0,0 +1,21 @@
+<?php
+
+namespace SimpleSAML\Test\Module\accounting\Entities;
+
+use SimpleSAML\Module\accounting\Entities\Bases\AbstractPayload;
+use SimpleSAML\Module\accounting\Entities\GenericJob;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * @covers \SimpleSAML\Module\accounting\Entities\GenericJob
+ * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractJob
+ */
+class GenericJobTest extends TestCase
+{
+    public function testCanGetProperType(): void
+    {
+        $job = new GenericJob($this->createStub(AbstractPayload::class));
+
+        $this->assertSame(GenericJob::class, $job->getType());
+    }
+}
diff --git a/tests/src/Stores/Jobs/DoctrineDbal/RawJobTest.php b/tests/src/Stores/Jobs/DoctrineDbal/JobsStore/RawJobTest.php
similarity index 90%
rename from tests/src/Stores/Jobs/DoctrineDbal/RawJobTest.php
rename to tests/src/Stores/Jobs/DoctrineDbal/JobsStore/RawJobTest.php
index 6d9fe229e710b367db088a8e3aa071d77b56ee9b..7615243deec2246d0de11e98973c8ff355ae8bff 100644
--- a/tests/src/Stores/Jobs/DoctrineDbal/RawJobTest.php
+++ b/tests/src/Stores/Jobs/DoctrineDbal/JobsStore/RawJobTest.php
@@ -1,17 +1,17 @@
 <?php
 
-namespace SimpleSAML\Test\Module\accounting\Stores\Jobs\DoctrineDbal;
+namespace SimpleSAML\Test\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore;
 
 use Doctrine\DBAL\Platforms\AbstractPlatform;
 use Doctrine\DBAL\Platforms\SqlitePlatform;
+use PHPUnit\Framework\TestCase;
 use SimpleSAML\Module\accounting\Entities\AuthenticationEvent;
 use SimpleSAML\Module\accounting\Exceptions\UnexpectedValueException;
 use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore;
-use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\RawJob;
-use PHPUnit\Framework\TestCase;
+use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore\RawJob;
 
 /**
- * @covers \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\RawJob
+ * @covers \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore\RawJob
  * @uses \SimpleSAML\Module\accounting\Entities\AuthenticationEvent
  */
 class RawJobTest extends TestCase
@@ -35,7 +35,7 @@ class RawJobTest extends TestCase
     public function testCanInstantiateValidRawJob(): void
     {
         $abstractPlatform = new SqlitePlatform();
-        $rawJob = new RawJob($this->validRawRow, $abstractPlatform);
+        $rawJob = new JobsStore\RawJob($this->validRawRow, $abstractPlatform);
         $this->assertSame($rawJob->getId(), $this->validRawRow[JobsStore::COLUMN_NAME_ID]);
         $this->assertEquals($rawJob->getPayload(), $this->authenticationEvent);
         $this->assertSame($rawJob->getType(), $this->validRawRow[JobsStore::COLUMN_NAME_TYPE]);
@@ -83,7 +83,7 @@ class RawJobTest extends TestCase
         $this->expectException(UnexpectedValueException::class);
 
         /** @psalm-suppress InvalidArgument */
-        new RawJob($invalidRawRow, $this->abstractPlatformStub);
+        new JobsStore\RawJob($invalidRawRow, $this->abstractPlatformStub);
     }
 
     public function testThrowsOnNonStringType(): void
@@ -94,7 +94,7 @@ class RawJobTest extends TestCase
         $this->expectException(UnexpectedValueException::class);
 
         /** @psalm-suppress InvalidArgument */
-        new RawJob($invalidRawRow, $this->abstractPlatformStub);
+        new JobsStore\RawJob($invalidRawRow, $this->abstractPlatformStub);
     }
 
     public function testThrowsOnNonStringCreatedAt(): void
@@ -116,6 +116,6 @@ class RawJobTest extends TestCase
         $this->expectException(UnexpectedValueException::class);
 
         /** @psalm-suppress InvalidArgument */
-        new RawJob($invalidRawRow, $this->abstractPlatformStub);
+        new JobsStore\RawJob($invalidRawRow, $this->abstractPlatformStub);
     }
 }
diff --git a/tests/src/Stores/Jobs/DoctrineDbal/JobsStore/RepositoryTest.php b/tests/src/Stores/Jobs/DoctrineDbal/JobsStore/RepositoryTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..11c09baa0fce5395a128a5e439977df5890926ad
--- /dev/null
+++ b/tests/src/Stores/Jobs/DoctrineDbal/JobsStore/RepositoryTest.php
@@ -0,0 +1,216 @@
+<?php
+
+namespace SimpleSAML\Test\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore;
+
+use SimpleSAML\Module\accounting\Entities\AuthenticationEvent;
+use SimpleSAML\Module\accounting\Entities\Bases\AbstractJob;
+use SimpleSAML\Module\accounting\Entities\Bases\AbstractPayload;
+use SimpleSAML\Module\accounting\Entities\GenericJob;
+use SimpleSAML\Module\accounting\Exceptions\StoreException;
+use SimpleSAML\Module\accounting\ModuleConfiguration;
+use SimpleSAML\Module\accounting\Services\LoggerService;
+use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
+use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory;
+use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator;
+use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore;
+use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore\Repository;
+use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpKernel\HttpCache\Store;
+
+/**
+ * @covers \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore\Repository
+ * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection
+ * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractJob
+ * @uses \SimpleSAML\Module\accounting\Helpers\FilesystemHelper
+ * @uses \SimpleSAML\Module\accounting\ModuleConfiguration
+ * @uses \SimpleSAML\Module\accounting\Stores\Connections\Bases\AbstractMigrator
+ * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Bases\AbstractMigration
+ * @uses \SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator
+ * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore
+ * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore\Migrations\Version20220601000000CreateJobsTable
+ * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore\Migrations\Version20220601000100CreateFailedJobsTable
+ * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore\RawJob
+ * @uses \SimpleSAML\Module\accounting\Entities\AuthenticationEvent
+ * @uses \SimpleSAML\Module\accounting\Entities\AuthenticationEvent\Job
+ */
+class RepositoryTest extends TestCase
+{
+    protected ModuleConfiguration $moduleConfiguration;
+    protected Connection $connection;
+    protected \PHPUnit\Framework\MockObject\Stub $loggerServiceStub;
+    protected Migrator $migrator;
+    protected \PHPUnit\Framework\MockObject\Stub $factoryStub;
+    protected \PHPUnit\Framework\MockObject\Stub $payloadStub;
+    protected \PHPUnit\Framework\MockObject\Stub $jobStub;
+    protected JobsStore $jobsStore;
+    protected string $jobsTableName;
+
+    protected function setUp(): void
+    {
+        // Configuration directory is set by phpunit using php ENV setting feature (check phpunit.xml).
+        $this->moduleConfiguration = new ModuleConfiguration('module_accounting.php');
+        $this->connection = new Connection(['driver' => 'pdo_sqlite', 'memory' => true,]);
+
+        $this->loggerServiceStub = $this->createStub(LoggerService::class);
+
+        /** @psalm-suppress InvalidArgument */
+        $this->migrator = new Migrator($this->connection, $this->loggerServiceStub);
+
+        $this->factoryStub = $this->createStub(Factory::class);
+        $this->factoryStub->method('buildConnection')->willReturn($this->connection);
+        $this->factoryStub->method('buildMigrator')->willReturn($this->migrator);
+
+        $this->payloadStub = $this->createStub(AbstractPayload::class);
+        $this->jobStub = $this->createStub(GenericJob::class);
+        $this->jobStub->method('getPayload')->willReturn($this->payloadStub);
+        $this->jobStub->method('getType')->willReturn(GenericJob::class);
+        $this->jobStub->method('getCreatedAt')->willReturn(new \DateTimeImmutable());
+
+        /** @psalm-suppress InvalidArgument */
+        $this->jobsStore = new JobsStore($this->moduleConfiguration, $this->factoryStub, $this->loggerServiceStub);
+
+        $this->jobsTableName = $this->connection->preparePrefixedTableName(JobsStore::TABLE_NAME_JOBS);
+    }
+
+    public function testCanInsertAndGetJob(): void
+    {
+        /** @psalm-suppress InvalidArgument */
+        $repository = new Repository($this->connection, $this->jobsTableName, $this->loggerServiceStub);
+        // Running setup will ensure that all migrations are ran.
+        $this->jobsStore->runSetup();
+
+        $this->assertNull($repository->getNext());
+
+        /** @psalm-suppress InvalidArgument */
+        $repository->insert($this->jobStub);
+
+        $this->assertNotNull($repository->getNext());
+    }
+
+    public function testInsertThrowsIfJobsStoreSetupNotRan(): void
+    {
+        /** @psalm-suppress InvalidArgument */
+        $repository = new Repository($this->connection, $this->jobsTableName, $this->loggerServiceStub);
+        // Running setup will ensure that all migrations are ran.
+        //$this->jobsStore->runSetup();
+
+        $this->expectException(StoreException::class);
+
+        /** @psalm-suppress InvalidArgument */
+        $repository->insert($this->jobStub);
+    }
+
+    public function testInsertThrowsForInvalidJobType(): void
+    {
+        /** @psalm-suppress InvalidArgument */
+        $repository = new Repository($this->connection, $this->jobsTableName, $this->loggerServiceStub);
+        // Running setup will ensure that all migrations are ran.
+        $this->jobsStore->runSetup();
+
+        $this->expectException(StoreException::class);
+
+        $invalidType = str_pad('abc', JobsStore::COLUMN_LENGTH_TYPE + 1);
+        $jobStub = $this->createStub(GenericJob::class);
+        $jobStub->method('getPayload')->willReturn($this->payloadStub);
+        $jobStub->method('getType')->willReturn($invalidType);
+        $jobStub->method('getCreatedAt')->willReturn(new \DateTimeImmutable());
+
+        /** @psalm-suppress InvalidArgument */
+        $repository->insert($jobStub);
+    }
+
+    public function testGetNextThrowsIfJobsStoreSetupNotRan(): void
+    {
+        /** @psalm-suppress InvalidArgument */
+        $repository = new Repository($this->connection, $this->jobsTableName, $this->loggerServiceStub);
+        // Running setup will ensure that all migrations are ran.
+        //$this->jobsStore->runSetup();
+
+        $this->expectException(StoreException::class);
+
+        $repository->getNext();
+    }
+
+    public function testGetNextThrowsForInvalidJobType(): void
+    {
+        /** @psalm-suppress InvalidArgument */
+        $repository = new Repository($this->connection, $this->jobsTableName, $this->loggerServiceStub);
+        // Running setup will ensure that all migrations are ran.
+        $this->jobsStore->runSetup();
+
+        $payloadStub = $this->createStub(AbstractPayload::class);
+        $jobStub = $this->createStub(AbstractJob::class); // Abstract classes can't be initialized..
+        $jobStub->method('getPayload')->willReturn($payloadStub);
+        $jobStub->method('getType')->willReturn(AbstractJob::class);
+        $jobStub->method('getCreatedAt')->willReturn(new \DateTimeImmutable());
+
+        /** @psalm-suppress InvalidArgument */
+        $repository->insert($jobStub);
+
+        $this->expectException(StoreException::class);
+
+        $repository->getNext();
+    }
+
+    public function testCanDeleteJob(): void
+    {
+        /** @psalm-suppress InvalidArgument */
+        $repository = new Repository($this->connection, $this->jobsTableName, $this->loggerServiceStub);
+        // Running setup will ensure that all migrations are ran.
+        $this->jobsStore->runSetup();
+
+        $this->assertFalse($repository->delete(1));
+        /** @psalm-suppress InvalidArgument */
+        $repository->insert($this->jobStub);
+        $job = $repository->getNext();
+        if ($job === null) {
+            throw new \Exception('Invalid job.');
+        }
+        $jobId = $job->getId();
+        if ($jobId === null) {
+            throw new \Exception('Invalid job ID.');
+        }
+        $this->assertTrue($repository->delete($jobId));
+        $this->assertFalse($repository->delete($jobId));
+    }
+
+    public function testDeleteThrowsWhenJobsStoreSetupNotRan(): void
+    {
+        /** @psalm-suppress InvalidArgument */
+        $repository = new Repository($this->connection, $this->jobsTableName, $this->loggerServiceStub);
+        // Running setup will ensure that all migrations are ran.
+        //$this->jobsStore->runSetup();
+
+        $this->expectException(StoreException::class);
+
+        $repository->delete(1);
+    }
+
+    public function testCanGetSpecificJobType(): void
+    {
+        /** @psalm-suppress InvalidArgument */
+        $repository = new Repository($this->connection, $this->jobsTableName, $this->loggerServiceStub);
+        // Running setup will ensure that all migrations are ran.
+        $this->jobsStore->runSetup();
+
+        $this->assertNull($repository->getNext());
+
+        /** @psalm-suppress InvalidArgument */
+        $repository->insert($this->jobStub);
+
+        $authenticationEvent = new AuthenticationEvent(['sample-state']);
+        $authenticationEventJob = new AuthenticationEvent\Job($authenticationEvent);
+
+        $repository->insert($authenticationEventJob);
+
+        $this->assertInstanceOf(AuthenticationEvent\Job::class, $repository->getNext(AuthenticationEvent\Job::class));
+    }
+
+    public function testInitializationThrowsForInvalidJobsTableName(): void
+    {
+        $this->expectException(StoreException::class);
+
+        /** @psalm-suppress InvalidArgument */
+        new Repository($this->connection, 'invalid-table-name', $this->loggerServiceStub);
+    }
+}
diff --git a/tests/src/Stores/Jobs/DoctrineDbal/JobsStoreTest.php b/tests/src/Stores/Jobs/DoctrineDbal/JobsStoreTest.php
index 3686a6f4acf623a132a381dcec22247e86dc9caa..d7c5a7db520e571d5a50f93de12e1e02f8540377 100644
--- a/tests/src/Stores/Jobs/DoctrineDbal/JobsStoreTest.php
+++ b/tests/src/Stores/Jobs/DoctrineDbal/JobsStoreTest.php
@@ -2,10 +2,11 @@
 
 namespace SimpleSAML\Test\Module\accounting\Stores\Jobs\DoctrineDbal;
 
+use PHPUnit\Framework\TestCase;
 use SimpleSAML\Module\accounting\Entities\AuthenticationEvent;
 use SimpleSAML\Module\accounting\Entities\Bases\AbstractJob;
 use SimpleSAML\Module\accounting\Entities\Bases\AbstractPayload;
-use SimpleSAML\Module\accounting\Entities\Interfaces\JobInterface;
+use SimpleSAML\Module\accounting\Entities\GenericJob;
 use SimpleSAML\Module\accounting\Exceptions\StoreException;
 use SimpleSAML\Module\accounting\ModuleConfiguration;
 use SimpleSAML\Module\accounting\Services\LoggerService;
@@ -13,7 +14,6 @@ use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Connection;
 use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Factory;
 use SimpleSAML\Module\accounting\Stores\Connections\DoctrineDbal\Migrator;
 use SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore;
-use PHPUnit\Framework\TestCase;
 
 /**
  * @covers \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore
@@ -27,9 +27,10 @@ use PHPUnit\Framework\TestCase;
  * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore\Migrations\Version20220601000000CreateJobsTable
  * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore\Migrations\Version20220601000100CreateFailedJobsTable
  * @uses \SimpleSAML\Module\accounting\Entities\Bases\AbstractJob
- * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\RawJob
+ * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore\RawJob
  * @uses \SimpleSAML\Module\accounting\Entities\AuthenticationEvent
  * @uses \SimpleSAML\Module\accounting\Entities\AuthenticationEvent\Job
+ * @uses \SimpleSAML\Module\accounting\Stores\Jobs\DoctrineDbal\JobsStore\Repository
  */
 class JobsStoreTest extends TestCase
 {
@@ -38,6 +39,8 @@ class JobsStoreTest extends TestCase
     protected Connection $connection;
     protected \PHPUnit\Framework\MockObject\Stub $loggerServiceStub;
     protected Migrator $migrator;
+    protected \PHPUnit\Framework\MockObject\Stub $payloadStub;
+    protected \PHPUnit\Framework\MockObject\Stub $jobStub;
 
     protected function setUp(): void
     {
@@ -53,6 +56,13 @@ class JobsStoreTest extends TestCase
         $this->factoryStub = $this->createStub(Factory::class);
         $this->factoryStub->method('buildConnection')->willReturn($this->connection);
         $this->factoryStub->method('buildMigrator')->willReturn($this->migrator);
+
+        $this->payloadStub = $this->createStub(AbstractPayload::class);
+        $this->jobStub = $this->createStub(GenericJob::class);
+        $this->jobStub->method('getPayload')->willReturn($this->payloadStub);
+        $this->jobStub->method('getType')->willReturn(GenericJob::class);
+        $this->jobStub->method('getCreatedAt')->willReturn(new \DateTimeImmutable());
+        $this->jobStub->method('getId')->willReturn(1);
     }
 
     public function testSetupDependsOnMigratorSetup(): void
@@ -101,21 +111,20 @@ class JobsStoreTest extends TestCase
         $jobsStore = new JobsStore($this->moduleConfiguration, $this->factoryStub, $this->loggerServiceStub);
         $jobsStore->runSetup();
 
-        $payloadStub = $this->createStub(AbstractPayload::class);
-        $jobStub = $this->createStub(AbstractJob::class);
-        $jobStub->method('getPayload')->willReturn($payloadStub);
-
         $queryBuilder = $this->connection->dbal()->createQueryBuilder();
         $queryBuilder->select('COUNT(id) as jobsCount')->from($jobsStore->getPrefixedTableNameJobs())->fetchOne();
 
         $this->assertSame(0, (int) $queryBuilder->executeQuery()->fetchOne());
 
-        $jobsStore->enqueue($jobStub);
+        /** @psalm-suppress InvalidArgument */
+        $jobsStore->enqueue($this->jobStub);
 
         $this->assertSame(1, (int) $queryBuilder->executeQuery()->fetchOne());
 
-        $jobsStore->enqueue($jobStub);
-        $jobsStore->enqueue($jobStub);
+        /** @psalm-suppress InvalidArgument */
+        $jobsStore->enqueue($this->jobStub);
+        /** @psalm-suppress InvalidArgument */
+        $jobsStore->enqueue($this->jobStub);
 
         $this->assertSame(3, (int) $queryBuilder->executeQuery()->fetchOne());
     }
@@ -136,81 +145,21 @@ class JobsStoreTest extends TestCase
         $jobsStore->enqueue($jobStub);
     }
 
-    public function testEnqueueThrowsStoreExceptionOnInvalidType(): void
-    {
-        /** @psalm-suppress InvalidArgument */
-        $jobsStore = new JobsStore($this->moduleConfiguration, $this->factoryStub, $this->loggerServiceStub);
-        // Don't run setup, so we get exception
-        $jobsStore->runSetup();
-
-        $payloadStub = $this->createStub(AbstractPayload::class);
-        $jobStub = $this->createStub(AbstractJob::class);
-        $jobStub->method('getPayload')->willReturn($payloadStub);
-        $invalidType = str_pad('invalid-type', JobsStore::COLUMN_LENGTH_TYPE + 1);
-
-        $this->expectException(StoreException::class);
-
-        $jobsStore->enqueue($jobStub, $invalidType);
-    }
-
-    public function testCanGetNextJob(): void
+    public function testCanDequeueJob(): void
     {
         /** @psalm-suppress InvalidArgument */
         $jobsStore = new JobsStore($this->moduleConfiguration, $this->factoryStub, $this->loggerServiceStub);
         $jobsStore->runSetup();
 
-        $payloadStub = $this->createStub(AbstractPayload::class);
-        $jobStub = $this->createStub(AbstractJob::class);
-        $jobStub->method('getPayload')->willReturn($payloadStub);
-
         $queryBuilder = $this->connection->dbal()->createQueryBuilder();
         $queryBuilder->select('COUNT(id) as jobsCount')->from($jobsStore->getPrefixedTableNameJobs())->fetchOne();
 
         $this->assertSame(0, (int) $queryBuilder->executeQuery()->fetchOne());
 
-        $jobsStore->enqueue($jobStub);
-
-        $this->assertNotEmpty($jobsStore->getNext()->fetchOne());
-        $this->assertNotEmpty($jobsStore->getNext(get_class($jobStub))->fetchOne());
-
-
-        $jobsStore->enqueue($jobStub, 'sample-type');
-        $this->assertNotEmpty($jobsStore->getNext('sample-type')->fetchOne());
-    }
-
-    public function testGetNextThrowsOnNonSetupRun(): void
-    {
         /** @psalm-suppress InvalidArgument */
-        $jobsStore = new JobsStore($this->moduleConfiguration, $this->factoryStub, $this->loggerServiceStub);
-        // Don't run setup, so we get exception
-        //$jobsStore->runSetup();
-
-        $payloadStub = $this->createStub(AbstractPayload::class);
-        $jobStub = $this->createStub(AbstractJob::class);
-        $jobStub->method('getPayload')->willReturn($payloadStub);
-
-        $this->expectException(StoreException::class);
-
-        $jobsStore->getNext();
-    }
-
-    public function testCanDequeueJob(): void
-    {
+        $jobsStore->enqueue($this->jobStub);
         /** @psalm-suppress InvalidArgument */
-        $jobsStore = new JobsStore($this->moduleConfiguration, $this->factoryStub, $this->loggerServiceStub);
-        $jobsStore->runSetup();
-
-        $payloadStub = $this->createStub(AbstractPayload::class);
-        $jobStub = $this->createStub(AbstractJob::class);
-        $jobStub->method('getPayload')->willReturn($payloadStub);
-
-        $queryBuilder = $this->connection->dbal()->createQueryBuilder();
-        $queryBuilder->select('COUNT(id) as jobsCount')->from($jobsStore->getPrefixedTableNameJobs())->fetchOne();
-
-        $this->assertSame(0, (int) $queryBuilder->executeQuery()->fetchOne());
-
-        $jobsStore->enqueue($jobStub);
-        $jobsStore->enqueue($jobStub);
+        $jobsStore->enqueue($this->jobStub);
 
         $this->assertSame(2, (int) $queryBuilder->executeQuery()->fetchOne());
 
@@ -225,10 +174,6 @@ class JobsStoreTest extends TestCase
         $jobsStore = new JobsStore($this->moduleConfiguration, $this->factoryStub, $this->loggerServiceStub);
         $jobsStore->runSetup();
 
-        $payloadStub = $this->createStub(AbstractPayload::class);
-        $jobStub = $this->createStub(AbstractJob::class);
-        $jobStub->method('getPayload')->willReturn($payloadStub);
-
         $authenticationEvent = new AuthenticationEvent(['sample-state']);
         $authenticationEventJob = new AuthenticationEvent\Job($authenticationEvent);
 
@@ -237,7 +182,8 @@ class JobsStoreTest extends TestCase
 
         $this->assertSame(0, (int) $queryBuilder->executeQuery()->fetchOne());
 
-        $jobsStore->enqueue($jobStub, 'test-type');
+        /** @psalm-suppress InvalidArgument */
+        $jobsStore->enqueue($this->jobStub);
         $jobsStore->enqueue($authenticationEventJob);
 
         $this->assertSame(2, (int) $queryBuilder->executeQuery()->fetchOne());
@@ -266,9 +212,66 @@ class JobsStoreTest extends TestCase
         $jobsStore->dequeue('test-type');
     }
 
+    public function testDequeueThrowsForJobWithInvalidId(): void
+    {
+        $repositoryStub = $this->createStub(JobsStore\Repository::class);
+        $jobStub = $this->createStub(GenericJob::class);
+        $jobStub->method('getPayload')->willReturn($this->payloadStub);
+        $jobStub->method('getCreatedAt')->willReturn(new \DateTimeImmutable());
+        $jobStub->method('getType')->willReturn(GenericJob::class);
+        $jobStub->method('getId')->willReturn(null); // Invalid ID value...
+
+        $repositoryStub->method('getNext')->willReturn($jobStub);
+
+        /** @psalm-suppress InvalidArgument */
+        $jobsStore = new JobsStore(
+            $this->moduleConfiguration,
+            $this->factoryStub,
+            $this->loggerServiceStub,
+            $repositoryStub
+        );
+        $jobsStore->runSetup();
+
+        $this->expectException(StoreException::class);
+
+        $jobsStore->dequeue();
+    }
+
+    public function testDequeThrowsAfterMaxDeleteAttempts(): void
+    {
+        $repositoryStub = $this->createStub(JobsStore\Repository::class);
+        $repositoryStub->method('getNext')->willReturn($this->jobStub);
+        $repositoryStub->method('delete')->willReturn(false);
+
+        /** @psalm-suppress InvalidArgument */
+        $jobsStore = new JobsStore(
+            $this->moduleConfiguration,
+            $this->factoryStub,
+            $this->loggerServiceStub,
+            $repositoryStub
+        );
+        $jobsStore->runSetup();
+
+        $this->expectException(StoreException::class);
+
+        $jobsStore->dequeue();
+    }
+
     public function testCanContinueSearchingInCaseOfJobDeletion(): void
     {
-        // TODO try to simulate job deletion
-        $this->markTestIncomplete();
+        $repositoryStub = $this->createStub(JobsStore\Repository::class);
+        $repositoryStub->method('getNext')->willReturn($this->jobStub);
+        $repositoryStub->method('delete')->willReturnOnConsecutiveCalls(false, true);
+
+        /** @psalm-suppress InvalidArgument */
+        $jobsStore = new JobsStore(
+            $this->moduleConfiguration,
+            $this->factoryStub,
+            $this->loggerServiceStub,
+            $repositoryStub
+        );
+        $jobsStore->runSetup();
+
+        $this->assertNotNull($jobsStore->dequeue());
     }
 }