diff --git a/app/Http/Controllers/CharacterController.php b/app/Http/Controllers/CharacterController.php index 28ca4a1..4d08753 100644 --- a/app/Http/Controllers/CharacterController.php +++ b/app/Http/Controllers/CharacterController.php @@ -28,6 +28,15 @@ class CharacterController extends Controller return view('character.create'); } + public function edit(Character $character) + { + $this->authorize('update', $character); + + return view('character.edit', [ + 'character' => $character + ]); + } + public function destroy(Character $character) { $this->authorize('delete', $character); diff --git a/app/Http/Livewire/Form/CreateCharacterForm.php b/app/Http/Livewire/Form/CreateCharacterForm.php index 60cdabf..846c989 100644 --- a/app/Http/Livewire/Form/CreateCharacterForm.php +++ b/app/Http/Livewire/Form/CreateCharacterForm.php @@ -8,41 +8,22 @@ use App\Warcraft\Races; use Livewire\Component; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; +use Illuminate\Validation\Rule; class CreateCharacterForm extends Component { use AuthorizesRequests; - /** - * Character's name - */ - public string $name = ''; - - /** - * Character's level - */ - public string $level = '70'; - /** * All possible classes */ public $classes; - /** - * Character's class - */ - public string $class = ''; - /** * All possible races */ public $races; - /** - * Character's race - */ - public string $race = ''; - /** * All posible genders */ @@ -52,28 +33,39 @@ class CreateCharacterForm extends Component ]; /** - * Character gender + * Character model */ - public $gender = 'M'; + public Character $character; /** - * Validation rules + * Get the validation rules that apply to the request. + * + * @return array */ - protected $rules = [ - 'name' => 'required|alpha|min:2|max:12|unique:characters,name', - 'level' => 'required|integer|min:1|max:70', - 'gender' => 'required|in:M,F', - 'race' => 'required', - 'class' => 'required' - ]; - - public function mount() + public function rules() { - $this->races = (new Races)->alliance(); - $rules['race'] = 'required|in:' . $this->races->keys()->join(','); + return [ + 'character.name' => [ 'required', 'min:4', Rule::unique('characters', 'name')->ignore($this->character) ], + 'character.level' => [ 'required', 'integer', 'min:1', 'max:70' ], + 'character.gender' => [ 'required', 'in:M,F' ], + 'character.race' => [ 'required', 'in:' . $this->races->keys()->join(',') ], + 'character.class' => [ 'required', 'in:' . $this->classes->keys()->join(',') ] + ]; + } - $this->race = $this->races->keys()->first(); - $this->updatedRace($this->race); + public function mount(Character $character) + { + $this->character = $character; + $this->races = (new Races)->alliance(); + + // Set some default values. + if (!$this->character->exists) { + $this->character->race = $this->races->keys()->first(); + $this->character->gender = 'M'; + $this->character->level = 70; + } + + $this->updatedCharacterRace($this->character->race); } public function updated($propertyName) @@ -81,18 +73,15 @@ class CreateCharacterForm extends Component $this->validateOnly($propertyName); } - public function updatedRace($race) + public function updatedCharacterRace($race) { // Update classes list for this race. - $this->classes = (new Classes)->race($this->race); - - // Update validation rules - $rules['class'] = 'required|in:' . $this->classes->keys()->join(','); + $this->classes = (new Classes)->race($race); // if this race can not be the selected class. // select the first one. - if (!$this->classes->has($this->class)) { - $this->class = $this->classes->keys()->first(); + if (!$this->classes->has($this->character->class)) { + $this->character->class = $this->classes->keys()->first(); } } @@ -101,16 +90,25 @@ class CreateCharacterForm extends Component */ public function save() { - $this->authorize('create', Character::class); + if ($this->character->exists) { + $this->authorize('update', $this->character); + $action = "updated"; + } else { + $this->authorize('create', Character::class); + $action = "created"; + } - $data = $this->validate(); + $this->validate(); - $user = auth()->user(); - $character = $user->characters()->create($data); + if ($action === 'created') { + $this->character->user() + ->associate(auth()->user()); + } + $this->character->save(); // Livewire redirect() does not have "with" method. // so we call session()->flash() directly instead. - session()->flash('success', "{$character->name} was created!"); + session()->flash('success', "{$this->character->name} was {$action}!"); return redirect()->route('user.index'); } diff --git a/resources/views/character/create.blade.php b/resources/views/character/create.blade.php index 4c32669..f91bc1a 100644 --- a/resources/views/character/create.blade.php +++ b/resources/views/character/create.blade.php @@ -3,7 +3,7 @@ {{ __('Add character') }}
- +
diff --git a/resources/views/character/edit.blade.php b/resources/views/character/edit.blade.php new file mode 100644 index 0000000..74ff4f9 --- /dev/null +++ b/resources/views/character/edit.blade.php @@ -0,0 +1,16 @@ + + + +
+ {{ __('Character') }} - + + {{ $character->level }} {{ $character->name }} + - {{ __('Edit') }} +
+
+ +
+ +
+ +
diff --git a/resources/views/livewire/form/character.blade.php b/resources/views/livewire/form/character.blade.php index ac31c8e..b13968c 100644 --- a/resources/views/livewire/form/character.blade.php +++ b/resources/views/livewire/form/character.blade.php @@ -5,34 +5,34 @@
{{ __('Name') }} - +
{{ __('Level') }} - +
{{ __('Race') }} - +
{{ __('Gender') }} - +
{{ __('Class') }} - +
- +
diff --git a/routes/web.php b/routes/web.php index 5f3ddc4..bdc532c 100644 --- a/routes/web.php +++ b/routes/web.php @@ -50,6 +50,7 @@ Route::prefix('characters')->name('character.')->group(function() { }); Route::get('/', [CharacterController::class, 'show'])->name('show'); + Route::get('/edit', [CharacterController::class, 'edit'])->name('edit'); Route::delete('/', [CharacterController::class, 'destroy'])->name('destroy'); }); }); diff --git a/tests/Feature/CharacterCreateTest.php b/tests/Feature/CharacterCreateTest.php index bde6be6..acc713b 100644 --- a/tests/Feature/CharacterCreateTest.php +++ b/tests/Feature/CharacterCreateTest.php @@ -37,11 +37,11 @@ class CharacterCreateTest extends TestCase $this->actingAs(User::factory()->create()); \Livewire::test(CreateCharacterForm::class) - ->set('name', 'Elise') - ->set('level', '70') - ->set('class', 'warrior') - ->set('race', 'human') - ->set('gender', 'F') + ->set('character.name', 'Elise') + ->set('character.level', '70') + ->set('character.class', 'warrior') + ->set('character.race', 'human') + ->set('character.gender', 'F') ->call('save') ->assertRedirect(route('user.index')); @@ -67,11 +67,11 @@ class CharacterCreateTest extends TestCase // Try create one more via livewire form. \Livewire::test(CreateCharacterForm::class) - ->set('name', 'Notonemore') - ->set('level', '10') - ->set('class', 'mage') - ->set('race', 'troll') - ->set('gender', 'M') + ->set('character.name', 'Notonemore') + ->set('character.level', '10') + ->set('character.class', 'mage') + ->set('character.race', 'troll') + ->set('character.gender', 'M') ->call('save') ->assertForbidden(); } @@ -79,11 +79,11 @@ class CharacterCreateTest extends TestCase public function test_guest_can_not_create_characters() { \Livewire::test(CreateCharacterForm::class) - ->set('name', 'Guestchar') - ->set('level', '61') - ->set('class', 'priest') - ->set('race', 'dwarf') - ->set('gender', 'M') + ->set('character.name', 'Guestchar') + ->set('character.level', '61') + ->set('character.class', 'priest') + ->set('character.race', 'dwarf') + ->set('character.gender', 'M') ->call('save') ->assertForbidden(); diff --git a/tests/Feature/CharacterUpdateTest.php b/tests/Feature/CharacterUpdateTest.php new file mode 100644 index 0000000..aeb378a --- /dev/null +++ b/tests/Feature/CharacterUpdateTest.php @@ -0,0 +1,123 @@ +create(); + $character = Character::factory()->for($user)->create(); + + $response = $this->actingAs($user) + ->get(route('character.edit', ['character' => $character])); + + $response->assertStatus(200); // OK + } + + public function test_guest_can_not_render_character_edit_page_for_someone_elses_character() + { + $user = User::factory()->create(); + $character = Character::factory()->create(); + + $response = $this->actingAs($user) + ->get(route('character.edit', ['character' => $character])); + + $response->assertStatus(403); // Forbidden + } + + public function test_user_can_update_character() + { + $user = User::factory()->create(); + $character = Character::factory()->for($user)->create([ + 'name' => 'Mychar', + 'level' => '60', + 'class' => 'warrior', + 'race' => 'human', + 'gender' => 'M' + ]); + + $this->actingAs($user); + + \Livewire::test(CreateCharacterForm::class, ['character' => $character]) + ->set('character.name', 'Updated') + ->set('character.level', '70') + ->set('character.class', 'priest') + ->set('character.race', 'dwarf') + ->set('character.gender', 'F') + ->call('save') + ->assertRedirect(route('user.index')); + + // Check database that character was updated. + $this->assertDatabaseHas('characters', [ + 'name' => 'Updated', + 'level' => '70', + 'race' => 'dwarf', + 'class' => 'priest', + 'gender' => 'F' + ]); + } + + public function test_user_can_not_update_someone_elses_character() + { + $user = User::factory()->create(); + $character = Character::factory()->create([ + 'race' => 'undead', + 'class' => 'rogue' + ]);; + + $this->actingAs($user); + + // Try update via livewire form. + \Livewire::test(CreateCharacterForm::class, ['character' => $character]) + ->set('character.name', 'Mynewname') + ->call('save') + ->assertForbidden(); + + // Check database that character has old data + $this->assertDatabaseHas('characters', [ + 'name' => $character->name, + 'level' => $character->level, + 'race' => $character->race, + 'class' => $character->class, + 'gender' => $character->gender + ]); + } + + public function test_guest_can_not_update_characters() + { + $character = Character::factory()->create([ + 'race' => 'troll', + 'class' => 'mage' + ]); + + \Livewire::test(CreateCharacterForm::class, ['character' => $character]) + ->set('character.name', 'Guestchar') + ->set('character.level', '61') + ->set('character.class', 'priest') + ->set('character.race', 'dwarf') + ->set('character.gender', 'M') + ->call('save') + ->assertForbidden(); + + // Check database that character has old data + $this->assertDatabaseHas('characters', [ + 'name' => $character->name, + 'level' => $character->level, + 'race' => $character->race, + 'class' => $character->class, + 'gender' => $character->gender + ]); + } +}