Skip to content
This repository was archived by the owner on Jul 2, 2020. It is now read-only.

Commit edeec9a

Browse files
committed
Working with a real database
1 parent 47cd9bd commit edeec9a

19 files changed

+497
-92
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.env

Diff for: README.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ Multi-purpose application for MultisolutiON collaborators.
66

77
| Stack | Lang | Runtime | Framework | Misc | Build |
88
| --- | --- | --- | --- | --- | --- |
9+
| `db` | [SQL](https://en.wikipedia.org/wiki/SQL) | [PostgreSQL](https://www.postgresql.org/) | [Flyway](https://flywaydb.org/) | | |
910
| `api` | [PHP](https://www.php.net/) | [Swoole](https://www.swoole.co.uk/) | [Siler](https://github.com/leocavalcante/siler) | [GraphQL](https://graphql.org/) | ![](https://github.com/multisolution/multi/workflows/API/badge.svg) |
1011
| `web` | [TypeScript](https://www.typescriptlang.org/) | [Next.js](https://nextjs.org/) | [React](https://reactjs.org/) | [Styled components](https://www.styled-components.com/) | ![](https://github.com/multisolution/multi/workflows/WEB/badge.svg) |

Diff for: api/.editorconfig

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
[*]
22
indent_size = 4
33
tab_width = 4
4+
5+
[*.yml]
6+
indent_size = 2
7+
tab_width = 2

Diff for: api/.env.dist

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
APP_DEBUG=false
2-
APP_KEY=
2+
APP_KEY=
3+
4+
APP_DB=pgsql
5+
APP_DSN=pgsql:host=multi_pgsql;port=5432;dbname=multi;user=multi;password=multi

Diff for: api/app/DoctrineDBAL.php

+264
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Multi;
4+
5+
use Doctrine\DBAL\Connection;
6+
use Doctrine\DBAL\Driver\ResultStatement;
7+
use Doctrine\DBAL\FetchMode;
8+
use Doctrine\DBAL\ParameterType;
9+
use DomainException;
10+
use Multi\Meeting\Meeting;
11+
use Multi\MeetingRoom\MeetingRoom;
12+
use Multi\User\User;
13+
use RuntimeException;
14+
15+
class DoctrineDBAL implements Database
16+
{
17+
/** @var Connection */
18+
private $conn;
19+
20+
public function __construct(Connection $conn)
21+
{
22+
$this->conn = $conn;
23+
}
24+
25+
public function userById(string $id): ?User
26+
{
27+
/** @var ResultStatement $stmt */
28+
$stmt = $this->conn->createQueryBuilder()
29+
->select('*')
30+
->from('users')
31+
->where('id = ?')
32+
->setParameter(0, $id)
33+
->execute();
34+
35+
$stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, User::class);
36+
$result = $stmt->fetch();
37+
38+
if ($result === false) {
39+
return null;
40+
}
41+
42+
return $result;
43+
}
44+
45+
public function userByEmail(string $email): ?User
46+
{
47+
$stmt = $this->conn->createQueryBuilder()
48+
->select('*')
49+
->from('users')
50+
->where('email = ?')
51+
->setParameter(0, $email)
52+
->execute();
53+
54+
$stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, User::class);
55+
$result = $stmt->fetch();
56+
57+
if ($result === false) {
58+
return null;
59+
}
60+
61+
return $result;
62+
}
63+
64+
public function insertUser(User $user): bool
65+
{
66+
/** @var int $affectedRows */
67+
$affectedRows = $this->conn->createQueryBuilder()
68+
->insert('users')
69+
->values([
70+
'id' => '?',
71+
'email' => '?',
72+
'password' => '?',
73+
])
74+
->setParameter(0, $user->id, ParameterType::STRING)
75+
->setParameter(1, $user->email, ParameterType::STRING)
76+
->setParameter(2, $user->password, ParameterType::STRING)
77+
->execute();
78+
79+
return $affectedRows > 0;
80+
}
81+
82+
public function meetingRoomById(string $id): ?MeetingRoom
83+
{
84+
$stmt = $this->conn->createQueryBuilder()
85+
->select('*')
86+
->from('meeting_rooms')
87+
->where('id = ?')
88+
->setParameter(0, $id)
89+
->execute();
90+
91+
$stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, MeetingRoom::class);
92+
$result = $stmt->fetch();
93+
94+
if ($result === false) {
95+
return null;
96+
}
97+
98+
return $result;
99+
}
100+
101+
public function insertMeetingRoom(MeetingRoom $meetingRoom): bool
102+
{
103+
$affectedRows = $this->conn->createQueryBuilder()
104+
->insert('meeting_rooms')
105+
->values([
106+
'id' => '?',
107+
'room_number' => '?',
108+
'description' => '?',
109+
])
110+
->setParameter(0, $meetingRoom->id)
111+
->setParameter(1, $meetingRoom->roomNumber)
112+
->setParameter(2, $meetingRoom->description)
113+
->execute();
114+
115+
return $affectedRows > 0;
116+
}
117+
118+
/**
119+
* @return MeetingRoom[]
120+
*/
121+
public function meetingRooms(): array
122+
{
123+
$stmt = $this->conn->createQueryBuilder()
124+
->select('*')
125+
->from('meeting_rooms')
126+
->execute();
127+
128+
$stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, MeetingRoom::class);
129+
return $stmt->fetchAll() ?? [];
130+
}
131+
132+
public function insertMeeting(Meeting $meeting): bool
133+
{
134+
$affectedRows = $this->conn->createQueryBuilder()
135+
->insert('meetings')
136+
->values([
137+
'id' => '?',
138+
'host_id' => '?',
139+
'room_id' => '?',
140+
'starts_at' => '?',
141+
'ends_at' => '?',
142+
])
143+
->setParameter(0, $meeting->id)
144+
->setParameter(1, $meeting->host->id)
145+
->setParameter(2, $meeting->room->id)
146+
->setParameter(3, $meeting->startsAt->format('Y-m-d H:i:s'))
147+
->setParameter(4, $meeting->endsAt->format('Y-m-d H:i:s'))
148+
->execute();
149+
150+
return $affectedRows > 0;
151+
}
152+
153+
/**
154+
* @param MeetingRoom $room
155+
* @return Meeting[]
156+
*/
157+
public function meetingsByRoom(MeetingRoom $room): array
158+
{
159+
$stmt = $this->conn->createQueryBuilder()
160+
->select('*')
161+
->from('meetings')
162+
->where('room_id = ?')
163+
->setParameter(0, $room->id)
164+
->execute();
165+
166+
$stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, Meeting::class);
167+
return $stmt->fetchAll() ?? [];
168+
}
169+
170+
/**
171+
* @param User $host
172+
* @return Meeting[]
173+
*/
174+
public function meetingsByHost(User $host): array
175+
{
176+
$stmt = $this->conn->createQueryBuilder()
177+
->select('*')
178+
->from('meetings')
179+
->where('host_id = ?')
180+
->setParameter(0, $host->id)
181+
->execute();
182+
183+
$stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, Meeting::class);
184+
return $stmt->fetchAll() ?? [];
185+
}
186+
187+
public function meetingRoomByNumber(int $roomNumber): ?MeetingRoom
188+
{
189+
$stmt = $this->conn->createQueryBuilder()
190+
->select('*')
191+
->from('meeting_rooms')
192+
->where('room_number = ?')
193+
->setParameter(0, $roomNumber)
194+
->execute();
195+
196+
$stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, MeetingRoom::class);
197+
$result = $stmt->fetch();
198+
199+
if ($result === false) {
200+
return null;
201+
}
202+
203+
return $result;
204+
}
205+
206+
public function meetingById(string $id): ?Meeting
207+
{
208+
$stmt = $this->conn->createQueryBuilder()
209+
->select('*')
210+
->from('meetings')
211+
->where('id = ?')
212+
->setParameter(0, $id)
213+
->execute();
214+
215+
$stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, Meeting::class);
216+
$result = $stmt->fetch();
217+
218+
if ($result === false) {
219+
return null;
220+
}
221+
222+
return $result;
223+
}
224+
225+
public function updateMeeting(?Meeting $meeting): bool
226+
{
227+
$affectedRows = $this->conn->createQueryBuilder()
228+
->update('meetings')
229+
->values([
230+
'room_id' => $meeting->room->id,
231+
'starts_at' => $meeting->startsAt->format('Y-m-d H:i:s'),
232+
'ends_at' => $meeting->endsAt->format('Y-m-d H:i:s'),
233+
]);
234+
235+
return $affectedRows > 0;
236+
}
237+
238+
public function meetingRoomByMeeting(Meeting $meeting): MeetingRoom
239+
{
240+
if ($meeting->room !== null) {
241+
return $meeting->room;
242+
}
243+
244+
if (!isset($meeting->room_id)) {
245+
throw new RuntimeException('Can not figure out meeting room');
246+
}
247+
248+
$stmt = $this->conn->createQueryBuilder()
249+
->select('*')
250+
->from('meeting_rooms')
251+
->where('id = ?')
252+
->setParameter(0, $meeting->room_id)
253+
->execute();
254+
255+
$stmt->setFetchMode(FetchMode::CUSTOM_OBJECT, MeetingRoom::class);
256+
$result = $stmt->fetch();
257+
258+
if ($result === false) {
259+
throw new DomainException('Associated room ID doest exists');
260+
}
261+
262+
return $result;
263+
}
264+
}

Diff for: api/app/dbs.php

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Multi;
4+
5+
use Doctrine\DBAL\DriverManager;
6+
7+
return [
8+
'in_memory' => function (string $_): Database {
9+
return new InMemoryDb();
10+
},
11+
'pgsql' => function (string $uri): Database {
12+
$params = ['url' => $uri];
13+
return new DoctrineDBAL(DriverManager::getConnection($params));
14+
},
15+
];

Diff for: api/docker-compose.yml

-7
This file was deleted.

Diff for: api/index.php

+5-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace Multi;
44

55
use Firebase\JWT\JWT;
6-
76
use Firebase\JWT\SignatureInvalidException;
87
use GraphQL\Error\Debug;
98
use GraphQL\Error\FormattedError;
@@ -16,12 +15,13 @@
1615
use function Siler\Encoder\Json\decode;
1716
use function Siler\Functional\Monad\maybe;
1817
use function Siler\GraphQL\{debug, execute, schema};
19-
use function Siler\Swoole\{bearer, http, json, raw,cors};
18+
use function Siler\Swoole\{bearer, cors, http, json, raw};
2019

2120
$base_dir = __DIR__;
2221
require_once "$base_dir/vendor/autoload.php";
2322

24-
Env\init($base_dir);
23+
$dbs = include "$base_dir/app/dbs.php";
24+
2525
Log\handler(new StreamHandler("$base_dir/app.log"));
2626

2727
$type_defs = file_get_contents("$base_dir/schema.graphql");
@@ -30,12 +30,11 @@
3030

3131
$context = new Context();
3232
$context->debug = Env\bool_val('APP_DEBUG', false);
33-
$context->db = new InMemoryDb();
33+
$context->db = $dbs[Env\env('APP_DB_USE', 'in_memory')](Env\env('APP_DB_URI'));
3434
$context->appKey = Env\env('APP_KEY');
3535
$context->id = new UniqueId();
3636
$context->messages = new InMemoryMessages();
3737

38-
include "$base_dir/seed.php";
3938
debug($context->debug ? Debug::INCLUDE_DEBUG_MESSAGE | Debug::INCLUDE_TRACE : 0);
4039

4140
$handler = function () use ($schema, $context) {
@@ -55,9 +54,8 @@
5554
} catch (Throwable $exception) {
5655
Log\error($exception->getMessage());
5756
$result = FormattedError::createFromException($exception);
58-
5957
} finally {
60-
cors('*','authorization,content-type');
58+
cors('*', 'authorization,content-type');
6159
json($result);
6260
}
6361
};

Diff for: api/resolvers.php

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
return [
1010
'Meeting' => [
1111
'status' => new Meeting\Status\Parser(),
12+
'room' => new Meeting\Room(),
1213
],
1314
'MeetingRoom' => [
1415
'calendar' => new MeetingRoom\Calendar(),

Diff for: api/seed.php

-16
This file was deleted.

0 commit comments

Comments
 (0)