diff --git a/src/Xhgui/Db/PdoRepository.php b/src/Xhgui/Db/PdoRepository.php new file mode 100644 index 000000000..6bfbbef31 --- /dev/null +++ b/src/Xhgui/Db/PdoRepository.php @@ -0,0 +1,167 @@ +pdo = $pdo; + $this->table = sprintf('"%s"', $table); + } + + public function getLatest(): array + { + $query = sprintf(' + SELECT + "id", + "profile", + "url", + "SERVER", + "GET", + "ENV", + "simple_url", + "request_ts", + "request_ts_micro," + "request_date" + FROM %s + ORDER BY "request_date" ASC + LIMIT 1', + $this->table + ); + $stmt = $this->pdo->query($query); + + $row = $stmt->fetch(PDO::FETCH_ASSOC); + if ($row === false) { + throw new RuntimeException('No profile available yet.'); + } + + return $row; + } + + public function getById(string $id): array + { + $query = sprintf(' + SELECT + "profile", + "url", + "SERVER", + "GET", + "ENV", + "simple_url", + "request_ts", + "request_ts_micro", + "request_date" + FROM %s + WHERE id = :id + ', $this->table); + $stmt = $this->pdo->prepare($query); + $stmt->execute(['id' => $id]); + + $row = $stmt->fetch(PDO::FETCH_ASSOC); + if ($row === false) { + throw new RuntimeException('No profile data found.'); + } + + return $row; + } + + public function countByUrl(string $url): int + { + $query = sprintf(' + SELECT COUNT(*) AS count + FROM %s + WHERE "simple_url" LIKE :url + ', $this->table); + $stmt = $this->pdo->prepare($query); + $stmt->execute(['url' => '%' . $url . '%']); + + return (int)$stmt->fetchColumn(); + } + + public function findByUrl(string $url, string $direction, int $skip, int $perPage): Generator + { + $query = sprintf(' + SELECT + "id", + "url", + "SERVER", + "GET", + "ENV", + "simple_url", + "request_ts", + "request_ts_micro", + "request_date", + "main_wt", + "main_ct", + "main_cpu", + "main_mu", + "main_pmu" + FROM %s + WHERE "simple_url" LIKE :url + ORDER BY "request_ts" %s + LIMIT %d OFFSET %d', + $this->table, + $direction, + $skip, + $perPage + ); + $stmt = $this->pdo->prepare($query); + $stmt->execute(['url' => '%' . $url . '%']); + + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + yield $row; + } + } + + public function deleteById(string $id) + { + $stmt = $this->pdo->prepare(sprintf(' + DELETE FROM %s + WHERE id = :id + ', $this->table)); + + $stmt->execute(['id' => $id]); + } + + public function deleteAll() + { + return is_int( + $this->pdo->exec(sprintf('DELETE FROM %s', $this->table)) + ); + } + + public function getStatistics() + { + $stmt = $this->pdo->query( + sprintf( + ' + SELECT + COUNT(*) AS profiles, + MAX("request_ts") AS latest, + SUM(LENGTH("profile")) AS bytes + FROM %s', + $this->table + ), + PDO::FETCH_ASSOC + ); + + $row = $stmt->fetch(PDO::FETCH_ASSOC); + + return $row ?: null; + } +} diff --git a/src/Xhgui/Searcher/PdoSearcher.php b/src/Xhgui/Searcher/PdoSearcher.php index 6cc89d962..ceedd2ad5 100644 --- a/src/Xhgui/Searcher/PdoSearcher.php +++ b/src/Xhgui/Searcher/PdoSearcher.php @@ -2,58 +2,25 @@ namespace XHGui\Searcher; -use Exception; -use PDO; +use XHGui\Db\PdoRepository; use XHGui\Profile; class PdoSearcher implements SearcherInterface { - /** - * @var PDO - */ - private $pdo; - - /** - * @var string - */ - private $table; + /** @var PdoRepository */ + private $db; - /** - * @param PDO $pdo An open database connection - * @param string $table Table name where Xhgui profiles are stored - */ - public function __construct(PDO $pdo, $table) + public function __construct(PdoRepository $db) { - $this->pdo = $pdo; - $this->table = $table; + $this->db = $db; } /** * {@inheritdoc} */ - public function latest() + public function latest(): Profile { - $stmt = $this->pdo->query(' - SELECT - "id", - "profile", - "url", - "SERVER", - "GET", - "ENV", - "simple_url", - "request_ts", - "request_ts_micro," - "request_date" - FROM "' . $this->table . '" - ORDER BY "request_date" ASC - LIMIT 1 - '); - - $row = $stmt->fetch(PDO::FETCH_ASSOC); - if (false === $row) { - throw new Exception('No profile available yet.'); - } + $row = $this->db->getLatest(); return new Profile([ '_id' => $row['id'], @@ -82,28 +49,9 @@ public function query($conditions, $limit, $fields = []) /** * {@inheritdoc} */ - public function get($id) + public function get($id): Profile { - $stmt = $this->pdo->prepare(' - SELECT - "profile", - "url", - "SERVER", - "GET", - "ENV", - "simple_url", - "request_ts", - "request_ts_micro", - "request_date" - FROM "' . $this->table . '" - WHERE id = :id - '); - - $stmt->execute(['id' => $id]); - - if (false === $row = $stmt->fetch(PDO::FETCH_ASSOC)) { - throw new Exception('No profile data found.'); - } + $row = $this->db->getById($id); return new Profile([ '_id' => $id, @@ -150,8 +98,6 @@ public function getAvgsForUrl($url, $search = []) */ public function getAll($options = []) { - $sort = $options['sort']; - $direction = $options['direction']; $page = (int)$options['page']; if ($page < 1) { $page = 1; @@ -159,44 +105,15 @@ public function getAll($options = []) $perPage = (int)$options['perPage']; $url = $options['conditions']['url'] ?? ""; - $stmt = $this->pdo->prepare(' - SELECT COUNT(*) AS count - FROM "' . $this->table . '" - WHERE "simple_url" LIKE :url - '); - $stmt->execute(['url' => '%'.$url.'%']); - $totalRows = (int)$stmt->fetchColumn(); - + $totalRows = $this->db->countByUrl($url); $totalPages = max(ceil($totalRows/$perPage), 1); if ($page > $totalPages) { $page = $totalPages; } $skip = ($page-1) * $perPage; - $stmt = $this->pdo->prepare(' - SELECT - "id", - "url", - "SERVER", - "GET", - "ENV", - "simple_url", - "request_ts", - "request_ts_micro", - "request_date", - "main_wt", - "main_ct", - "main_cpu", - "main_mu", - "main_pmu" - FROM "' . $this->table . '" - WHERE "simple_url" LIKE :url - ORDER BY "request_ts" ' . $direction. ' - LIMIT ' . $skip . ' OFFSET ' . $perPage); - $stmt->execute(['url' => '%'.$url.'%']); - $results = []; - while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + foreach ($this->db->findByUrl($url, $direction, $skip, $perPage) as $row) { $results[] = new Profile([ '_id' => $row['id'], 'meta' => [ @@ -236,12 +153,7 @@ public function getAll($options = []) */ public function delete($id) { - $stmt = $this->pdo->prepare(' - DELETE FROM "' . $this->table . '" - WHERE id = :id - '); - - $stmt->execute(['id' => $id]); + $this->db->deleteById($id); } /** @@ -249,9 +161,7 @@ public function delete($id) */ public function truncate() { - return is_int( - $this->pdo->exec('DELETE FROM "' . $this->table . '"') - ); + return $this->db->deleteAll(); } /** @@ -282,17 +192,9 @@ public function truncateWatches() */ public function stats() { - $stmt = $this->pdo->query(' - SELECT - COUNT(*) AS profiles, - MAX("request_ts") AS latest, - SUM(LENGTH("profile")) AS bytes - FROM "' . $this->table . '" - ', PDO::FETCH_ASSOC); - - $row = $stmt->fetch(PDO::FETCH_ASSOC); + $row = $this->db->getStatistics(); - if (false === $row) { + if (!$row) { $row = [ 'profiles' => 0, 'latest' => 0, diff --git a/src/Xhgui/ServiceContainer.php b/src/Xhgui/ServiceContainer.php index a86de4700..05d9930ac 100644 --- a/src/Xhgui/ServiceContainer.php +++ b/src/Xhgui/ServiceContainer.php @@ -10,6 +10,7 @@ use Slim\Middleware\SessionCookie; use Slim\Slim; use Slim\Views\Twig; +use XHGui\Db\PdoRepository; use XHGui\Middleware\RenderMiddleware; use XHGui\Searcher\MongoSearcher; use XHGui\Searcher\PdoSearcher; @@ -129,12 +130,16 @@ protected function _services() ); }; + $this[PdoRepository::class] = static function ($c) { + return new PdoRepository($c['pdo'], $c['config']['pdo']['table']); + }; + $this['searcher.mongodb'] = static function ($c) { return new MongoSearcher($c['db']); }; $this['searcher.pdo'] = static function ($c) { - return new PdoSearcher($c['pdo'], $c['config']['pdo']['table']); + return new PdoSearcher($c[PdoRepository::class]); }; $this['searcher'] = static function ($c) {