From 9a0697ce4207467c6952fd62bc690e4a728eaf84 Mon Sep 17 00:00:00 2001 From: Alpha Olomi Date: Sat, 22 Jul 2023 03:11:00 +0300 Subject: [PATCH] update --- .github/ISSUE_TEMPLATE/bug.yml | 66 ++++++++++++++++++++ .github/ISSUE_TEMPLATE/config.yml | 9 +-- .github/workflows/run-tests.yml | 16 +++-- config/notes.php | 11 +++- database/factories/NoteFactory.php | 64 ++++++++++++++++++- database/migrations/create_notes_table.php | 10 ++- src/Concerns/HasNotes.php | 70 +++++++++++++++++++-- src/Contracts/IsNote.php | 7 +++ src/Models/Note.php | 2 +- tests/NoteModelTest.php | 71 ++++++++++++++++++++++ tests/NoteTest.php | 12 ++-- 11 files changed, 309 insertions(+), 29 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/bug.yml create mode 100644 tests/NoteModelTest.php diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..fe4cfe6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,66 @@ +name: Bug Report +description: Report an Issue or Bug with the Package +title: "[Bug]: " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + We're sorry to hear you have a problem. Can you help us solve it by providing the following details. + - type: textarea + id: what-happened + attributes: + label: What happened? + description: What did you expect to happen? + placeholder: I cannot currently do X thing because when I do, it breaks X thing. + validations: + required: true + - type: textarea + id: how-to-reproduce + attributes: + label: How to reproduce the bug + description: How did this occur, please add any config values used and provide a set of reliable steps if possible. + placeholder: When I do X I see Y. + validations: + required: true + - type: input + id: package-version + attributes: + label: Package Version + description: What version of our Package are you running? Please be as specific as possible + placeholder: 2.0.0 + validations: + required: true + - type: input + id: php-version + attributes: + label: PHP Version + description: What version of PHP are you running? Please be as specific as possible + placeholder: 8.2.0 + validations: + required: true + - type: input + id: laravel-version + attributes: + label: Laravel Version + description: What version of Laravel are you running? Please be as specific as possible + placeholder: 9.0.0 + validations: + required: true + - type: dropdown + id: operating-systems + attributes: + label: Which operating systems does with happen with? + description: You may select more than one. + multiple: true + options: + - macOS + - Windows + - Linux + - type: textarea + id: notes + attributes: + label: Notes + description: Use this field to provide any other notes that you feel might be relevant to the issue. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 6c071f0..26f7f98 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,14 +1,11 @@ blank_issues_enabled: false contact_links: - name: Ask a question - url: https://github.com/ryangjchandler/laravel-notes/discussions/new?category=q-a + url: https://github.com/alphaolomi/laravel-notes/discussions/new?category=q-a about: Ask the community for help - name: Request a feature - url: https://github.com/ryangjchandler/laravel-notes/discussions/new?category=ideas + url: https://github.com/alphaolomi/laravel-notes/discussions/new?category=ideas about: Share ideas for new features - name: Report a security issue - url: https://github.com/ryangjchandler/laravel-notes/security/policy + url: https://github.com/alphaolomi/laravel-notes/security/policy about: Learn how to notify us for sensitive bugs - - name: Report a bug - url: https://github.com/ryangjchandler/laravel-notes/issues/new - about: Report a reproducable bug diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 39ff7ee..b5d6720 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -13,12 +13,13 @@ jobs: fail-fast: true matrix: os: [ubuntu-latest, windows-latest] - php: [8.1] - laravel: [9.*] + php: [8.2, 8.1] + laravel: [10.*] stability: [prefer-lowest, prefer-stable] include: - - laravel: 9.* - testbench: 7.* + - laravel: 10.* + testbench: 8.* + carbon: ^2.63 name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} @@ -40,8 +41,11 @@ jobs: - name: Install dependencies run: | - composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update + composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" "nesbot/carbon:${{ matrix.carbon }}" --no-interaction --no-update composer update --${{ matrix.stability }} --prefer-dist --no-interaction + - name: List Installed Dependencies + run: composer show -D + - name: Execute tests - run: vendor/bin/pest + run: vendor/bin/pest --ci diff --git a/config/notes.php b/config/notes.php index 08631bf..7acdd13 100644 --- a/config/notes.php +++ b/config/notes.php @@ -2,9 +2,16 @@ return [ + /** + * The note model class name. + */ 'model' => \AlphaOlomi\Notes\Models\Note::class, - /** @phpstan-ignore-next-line */ - 'user' => \App\Models\User::class, + /** + * The user model class name. This is the model that will be used to + * represent the authors of the notes. By default this is the default + * user model that ships with Laravel. + */ + 'user_model' => config('auth.providers.users.model') ]; diff --git a/database/factories/NoteFactory.php b/database/factories/NoteFactory.php index d0c4e37..a7a91ae 100644 --- a/database/factories/NoteFactory.php +++ b/database/factories/NoteFactory.php @@ -7,12 +7,74 @@ class NoteFactory extends Factory { + /** + * The name of the factory's corresponding model. + * + * @var string + */ protected $model = Note::class; + /** + * Define the model's default state. + * + * @return array + */ public function definition() { return [ - 'content' => $this->faker->words(rand(3, 10), asText: true), + 'parent_id' => null, // Set to null for a root note + 'user_id' => config('notes.user_model')::factory(), + 'notable_type' => null, // Replace with the desired polymorphic type + 'notable_id' => null, // Replace with the desired polymorphic ID + 'content' => $this->faker->paragraph(), ]; } + + /** + * Indicate that the note has a parent. + * + * @return \Illuminate\Database\Eloquent\Factories\Factory + */ + public function withParent() + { + return $this->state(function (array $attributes) { + return [ + 'parent_id' => Note::factory(), + ]; + }); + } + + /** + * Indicate the type and ID of the related polymorphic model. + * + * @param string $type + * @param int $id + * @return \Illuminate\Database\Eloquent\Factories\Factory + */ + public function withNotable($type, $id) + { + return $this->state(function (array $attributes) use ($type, $id) { + return [ + 'notable_type' => $type, + 'notable_id' => $id, + ]; + }); + } + + /** + * Indicate the related polymorphic model. + * + * @param string $type + * @param int $id + * @return \Illuminate\Database\Eloquent\Factories\Factory + */ + public function withModelNotable($model) + { + return $this->state(function (array $attributes) use ($model) { + return [ + 'notable_type' => $model::class, + 'notable_id' => $model::factory(), + ]; + }); + } } diff --git a/database/migrations/create_notes_table.php b/database/migrations/create_notes_table.php index cd2514d..04fa185 100644 --- a/database/migrations/create_notes_table.php +++ b/database/migrations/create_notes_table.php @@ -11,11 +11,15 @@ public function up() Schema::create('notes', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('parent_id')->nullable()->index(); - $table->unsignedBigInteger('user_id')->index()->nullable(); - $table->morphs('notable'); - $table->longText('content'); + $table->unsignedBigInteger('user_id')->nullable()->index(); + $table->nullableMorphs('notable'); + $table->string('title'); + $table->longText('content')->nullable(); $table->timestamps(); $table->softDeletes(); + + // Add foreign key constraints + $table->foreign('parent_id')->references('id')->on('notes')->onDelete('set null'); }); } }; diff --git a/src/Concerns/HasNotes.php b/src/Concerns/HasNotes.php index b6ce47b..815b335 100644 --- a/src/Concerns/HasNotes.php +++ b/src/Concerns/HasNotes.php @@ -8,22 +8,84 @@ use Illuminate\Support\Facades\Auth; /** + * Notes trait. + * + * @property-read MorphMany $notes + * @property-read MorphMany $allNotes + * @property-read MorphMany $rootNotes + * @property-read MorphMany $allRootNotes + * @property-read MorphMany $firstNote + * * @mixin \Illuminate\Database\Eloquent\Model */ trait HasNotes { - /** @return MorphMany */ + /** + * Get all of the model's notes. + * + * @return MorphMany + */ public function notes(): MorphMany { return $this->morphMany(config('notes.model'), 'notable'); } - public function addNote(string $content, Model $user = null, IsNote $parent = null): IsNote + /** + * Get all of the model's notes. + * + * @return MorphMany + */ + public function allNotes(): MorphMany { - return $this->notes()->create([ + return $this->notes()->with('children'); + } + + /** + * Get all of the model's notes. + * + * @return MorphMany + */ + public function rootNotes(): MorphMany + { + return $this->notes()->whereNull('parent_id'); + } + + /** + * Get all of the model's notes. + * + * @return MorphMany + */ + public function allRootNotes(): MorphMany + { + return $this->allNotes()->whereNull('parent_id'); + } + + /** + * Get first note of the model. + * + * @return MorphMany + */ + public function firstNote(): MorphMany + { + return $this->notes()->first(); + } + + /** + * Add a note to the model. + * + * @param string $content + * @param Model|null $user + * @param IsNote|null $parent + * @return IsNote + */ + public function addNote(string $content, Model $user = null, IsNote $parent = null) + { + $note = $this->notes()->create([ 'content' => $content, 'user_id' => $user ? $user->getKey() : Auth::id(), - 'parent_id' => $parent?->getKey(), + 'parent_id' => $parent ? $parent?->getKey() : null, ]); + + return $note; } } diff --git a/src/Contracts/IsNote.php b/src/Contracts/IsNote.php index e95cc64..ded4c84 100644 --- a/src/Contracts/IsNote.php +++ b/src/Contracts/IsNote.php @@ -7,6 +7,13 @@ use Illuminate\Database\Eloquent\Relations\MorphTo; /** + * Note contract. + * + * @property-read MorphTo $notes + * @property-read BelongsTo $parent + * @property-read HasMany $children + * @property-read BelongsTo $user + * * @mixin \Illuminate\Database\Eloquent\Model */ interface IsNote diff --git a/src/Models/Note.php b/src/Models/Note.php index 18509fe..29e2dfb 100644 --- a/src/Models/Note.php +++ b/src/Models/Note.php @@ -24,7 +24,7 @@ public function notes(): MorphTo public function user(): BelongsTo { - return $this->belongsTo(config('notes.user'), 'user_id'); + return $this->belongsTo(config('notes.user_model'), 'user_id'); } public function parent(): BelongsTo diff --git a/tests/NoteModelTest.php b/tests/NoteModelTest.php new file mode 100644 index 0000000..1366d42 --- /dev/null +++ b/tests/NoteModelTest.php @@ -0,0 +1,71 @@ +id(); + $table->timestamps(); + }); + } +} + +it('can be created', function () { + + $note = Note::create([ + 'title' => 'Hello, world!', + 'content' => 'Hello, world!', + ]); + + assertDatabaseHas('notes', [ + 'id' => $note->getKey(), + 'title' => 'Hello, world!', + 'content' => 'Hello, world!', + ]); +}); + +it('can belong to a user', function () { + actingAsUser(); + + $note = Note::create([ + 'title' => 'Hello, world!', + 'content' => 'Hello, world!', + 'user_id' => auth()->id(), + ]); + + expect($note->user)->toBe(auth()->user()); + + assertDatabaseHas('notes', [ + 'id' => $note->getKey(), + 'content' => 'Great project!', + 'user_id' => auth()->id(), + ]); +}); + +it('can belong to another note', function () { + $project = Project::create(); + $parentNote = $project->addNote('Hello, world!'); + $childNote = $project->addNote('Well Done!', parent: $parentNote); + + expect($parentNote->children)->toHaveCount(1); + + expect($childNote->parent)->toBe($parentNote); + + assertDatabaseHas('notes', [ + 'id' => $childNote->getKey(), + 'content' => 'Well Done!', + 'parent_id' => $parentNote->getKey(), + ]); +}); diff --git a/tests/NoteTest.php b/tests/NoteTest.php index ae861c6..f2d800a 100644 --- a/tests/NoteTest.php +++ b/tests/NoteTest.php @@ -7,14 +7,14 @@ use function Pest\Laravel\assertDatabaseHas; -class Project extends Model +class Doc extends Model { use HasNotes; public static function booted() { - Schema::dropIfExists('projects'); - Schema::create('projects', function (Blueprint $table) { + Schema::dropIfExists('docs'); + Schema::create('docs', function (Blueprint $table) { $table->id(); $table->timestamps(); }); @@ -22,7 +22,7 @@ public static function booted() } it('can be created', function () { - $project = Project::create(); + $project = Doc::create(); $note = $project->addNote('Hello, world!'); assertDatabaseHas('notes', [ @@ -34,7 +34,7 @@ public static function booted() it('can belong to a user', function () { actingAsUser(); - $project = Project::create(); + $project = Doc::create(); $note = $project->addNote('Great project!'); assertDatabaseHas('notes', [ @@ -45,7 +45,7 @@ public static function booted() }); it('can belong to another note', function () { - $project = Project::create(); + $project = Doc::create(); $parent = $project->addNote('Hello, world!'); $child = $project->addNote('Well Done!', parent: $parent);