diff --git a/db/migrations/2019_11_25_000000_create_messages_table.php b/db/migrations/2019_11_25_000000_create_messages_table.php new file mode 100644 index 00000000..b34a6c19 --- /dev/null +++ b/db/migrations/2019_11_25_000000_create_messages_table.php @@ -0,0 +1,160 @@ +schema->hasTable('Messages'); + + if ($hasPreviousMessagesTable) { + // Rename because some SQL DBMS handle identifiers case insensitive + $this->schema->rename('Messages', 'PreviousMessages'); + } + + $this->createNewMessagesTable(); + + if ($hasPreviousMessagesTable) { + $this->copyPreviousToNewMessagesTable(); + $this->changeReferences( + 'PreviousMessages', + 'ID', + 'messages', + 'id', + 'unsignedInteger' + ); + $this->schema->drop('PreviousMessages'); + } + } + + /** + * Recreates the previous "Messages" table, copies back the data and drops the new "messages" table. + */ + public function down(): void + { + // Rename as some SQL DBMS handle identifiers case insensitive + $this->schema->rename('messages', 'new_messages'); + + $this->createPreviousMessagesTable(); + $this->copyNewToPreviousMessagesTable(); + $this->changeReferences( + 'new_messages', + 'id', + 'Messages', + 'ID', + 'unsignedInteger' + ); + + $this->schema->drop('new_messages'); + } + + /** + * @return void + */ + private function createNewMessagesTable(): void + { + $this->schema->create( + 'messages', + function (Blueprint $table) { + $table->increments('id'); + $this->references($table, 'users', 'user_id'); + $this->references($table, 'users', 'receiver_id'); + $table->boolean('read')->default(0); + $table->text('text'); + $table->timestamps(); + } + ); + } + + /** + * @return void + */ + private function copyPreviousToNewMessagesTable(): void + { + $connection = $this->schema->getConnection(); + /** @var stdClass[] $previousMessageRecords */ + $previousMessageRecords = $connection + ->table('PreviousMessages') + ->get(); + + foreach ($previousMessageRecords as $previousMessage) { + $date = Carbon::createFromTimestamp($previousMessage->Datum); + $connection->table('messages')->insert( + [ + 'id' => $previousMessage->id, + 'user_id' => $previousMessage->SUID, + 'receiver_id' => $previousMessage->RUID, + 'read' => $previousMessage->isRead === 'N' ? 0 : 1, + 'text' => $previousMessage->Text, + 'created_at' => $date, + 'updated_at' => $date, + ] + ); + } + } + + /** + * @return void + */ + private function createPreviousMessagesTable(): void + { + $this->schema->create( + 'Messages', + function (Blueprint $table) { + $table->increments('id'); + $table->integer('Datum'); + $this->references($table, 'users', 'SUID'); + $this->references($table, 'users', 'RUID'); + $table->char('isRead') + ->default('N'); + $table->text('Text'); + } + ); + } + + /** + * @return void + */ + private function copyNewToPreviousMessagesTable(): void + { + $connection = $this->schema->getConnection(); + /** @var Collection|stdClass[] $messageRecords */ + $messageRecords = $connection + ->table('new_messages') + ->get(); + + foreach ($messageRecords as $messageRecord) { + $date = Carbon::createFromFormat('Y-m-d H:i:s', $messageRecord->created_at) + ->getTimestamp(); + + $connection->table('Messages')->insert( + [ + 'id' => $messageRecord->id, + 'Datum' => $date, + 'SUID' => $messageRecord->user_id, + 'RUID' => $messageRecord->receiver_id, + 'isRead' => $messageRecord->read === 0 ? 'N' : 'Y', + 'Text' => $messageRecord->text, + ] + ); + } + } +} diff --git a/src/Models/Message.php b/src/Models/Message.php new file mode 100644 index 00000000..cb658fc6 --- /dev/null +++ b/src/Models/Message.php @@ -0,0 +1,68 @@ + 'integer', + 'receiver_id' => 'integer', + 'read' => 'boolean', + ]; + + /** @var string[] */ + protected $fillable = [ + 'user_id', + 'receiver_id', + 'read', + 'text', + ]; + + /** @var array */ + protected $attributes = [ + 'read' => false, + ]; + + /** + * @return BelongsTo + */ + public function receiver(): BelongsTo + { + return $this->belongsTo(User::class, 'receiver_id'); + } +} diff --git a/src/Models/User/User.php b/src/Models/User/User.php index e2ee9b21..c4bc1fcb 100644 --- a/src/Models/User/User.php +++ b/src/Models/User/User.php @@ -4,6 +4,7 @@ namespace Engelsystem\Models\User; use Carbon\Carbon; use Engelsystem\Models\BaseModel; +use Engelsystem\Models\Message; use Engelsystem\Models\News; use Engelsystem\Models\NewsComment; use Engelsystem\Models\Question; @@ -42,6 +43,9 @@ use Illuminate\Database\Query\Builder as QueryBuilder; * * @property-read Collection|Question[] $questionsAsked * @property-read Collection|Question[] $questionsAnswered + * @property-read Collection|Message[] $messagesReceived + * @property-read Collection|Message[] $messagesSent + * @property-read Collection|Message[] $messages */ class User extends BaseModel { @@ -141,4 +145,38 @@ class User extends BaseModel return $this->hasMany(Question::class, 'answerer_id') ->where('answerer_id', $this->id); } + + /** + * @return HasMany + */ + public function messagesSent(): HasMany + { + return $this->hasMany(Message::class, 'user_id') + ->orderBy('created_at', 'DESC') + ->orderBy('id', 'DESC'); + } + + /** + * @return HasMany + */ + public function messagesReceived(): HasMany + { + return $this->hasMany(Message::class, 'receiver_id') + ->orderBy('read') + ->orderBy('created_at', 'DESC') + ->orderBy('id', 'DESC'); + } + + /** + * Returns a HasMany relation for all messages sent or received by the user. + * + * @return HasMany + */ + public function messages(): HasMany + { + return $this->messagesSent() + ->union($this->messagesReceived()) + ->orderBy('read') + ->orderBy('id', 'DESC'); + } } diff --git a/tests/Unit/Models/MessageTest.php b/tests/Unit/Models/MessageTest.php new file mode 100644 index 00000000..11af9a83 --- /dev/null +++ b/tests/Unit/Models/MessageTest.php @@ -0,0 +1,171 @@ +initDatabase(); + + $this->user1 = User::create([ + 'name' => 'user1', + 'password' => '', + 'email' => 'user1@example.com', + 'api_key' => '', + ]); + + $this->user2 = User::create([ + 'name' => 'user2', + 'password' => '', + 'email' => 'user2@example.com', + 'api_key' => '', + ]); + + $this->message1 = Message::create([ + 'user_id' => $this->user1->id, + 'receiver_id' => $this->user2->id, + 'text' => 'message1', + ]); + + $this->message2 = Message::create([ + 'user_id' => $this->user1->id, + 'receiver_id' => $this->user2->id, + 'read' => true, + 'text' => 'message2', + ]); + + $this->message3 = Message::create([ + 'user_id' => $this->user2->id, + 'receiver_id' => $this->user1->id, + 'text' => 'message3', + ]); + } + + /** + * Tests that loading Messages works. + * + * @return void + */ + public function testLoad(): void + { + $message1 = Message::find($this->message1->id); + $this->assertSame($this->message1->user_id, $message1->user_id); + $this->assertSame($this->message1->receiver_id, $message1->receiver_id); + $this->assertSame($this->message1->read, $message1->read); + $this->assertSame($this->message1->text, $message1->text); + + $message2 = Message::find($this->message2->id); + $this->assertSame($this->message2->user_id, $message2->user_id); + $this->assertSame($this->message2->receiver_id, $message2->receiver_id); + $this->assertSame($this->message2->read, $message2->read); + $this->assertSame($this->message2->text, $message2->text); + } + + /** + * Tests that the Messages have the correct senders. + * + * @return void + */ + public function testSenders(): void + { + $this->assertSame($this->user1->id, $this->message1->user->id); + $this->assertSame($this->user1->id, $this->message2->user->id); + $this->assertSame($this->user2->id, $this->message3->user->id); + } + + /** + * Tests that the Messages have the correct receivers. + * + * @return void + */ + public function testReceivers(): void + { + $this->assertSame($this->user2->id, $this->message1->receiver->id); + $this->assertSame($this->user2->id, $this->message2->receiver->id); + $this->assertSame($this->user1->id, $this->message3->receiver->id); + } + + /** + * Tests that the Users have the correct sent Messages. + * + * @return void + */ + public function testUserSentMessages(): void + { + $sentByUser1 = $this->user1->messagesSent->all(); + $this->assertCount(2, $sentByUser1); + $this->assertSame($this->message2->id, $sentByUser1[0]->id); + $this->assertSame($this->message1->id, $sentByUser1[1]->id); + + $sentByUser2 = $this->user2->messagesSent->all(); + $this->assertCount(1, $sentByUser2); + $this->assertSame($this->message3->id, $sentByUser2[0]->id); + } + + /** + * Tests that the Users have the correct received Messages. + * + * @return void + */ + public function testUserReceivedMessages(): void + { + $receivedByUser1 = $this->user1->messagesReceived->all(); + $this->assertCount(1, $receivedByUser1); + $this->assertSame($this->message3->id, $receivedByUser1[0]->id); + + $receivedByUser2 = $this->user2->messagesReceived->all(); + $this->assertCount(2, $receivedByUser2); + $this->assertSame($this->message1->id, $receivedByUser2[0]->id); + $this->assertSame($this->message2->id, $receivedByUser2[1]->id); + } + + /** + * Tests that the user have the correct Messages. + */ + public function testUserMessages(): void + { + $user1Messages = $this->user1->messages->all(); + $this->assertCount(3, $user1Messages); + $this->assertSame($this->message3->id, $user1Messages[0]->id); + $this->assertSame($this->message1->id, $user1Messages[1]->id); + $this->assertSame($this->message2->id, $user1Messages[2]->id); + + $user2Messages = $this->user2->messages->all(); + $this->assertCount(3, $user2Messages); + $this->assertSame($this->message3->id, $user2Messages[0]->id); + $this->assertSame($this->message1->id, $user2Messages[1]->id); + $this->assertSame($this->message2->id, $user2Messages[2]->id); + } +}