Skip to content

Commit b6e28a1

Browse files
committed
Card: index, create, edit, delete | Improve test coverage | Use locale helper wherever necessary
1 parent bbcede3 commit b6e28a1

30 files changed

+879
-111
lines changed

app/Livewire/Cards/Create.php

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,46 @@
22

33
namespace App\Livewire\Cards;
44

5+
use App\Models\Deck;
6+
use Illuminate\Validation\Rule;
57
use Livewire\Component;
68

79
class Create extends Component
810
{
9-
public function render()
11+
public Deck $deck;
12+
13+
public string $question = '';
14+
15+
public string $answer = '';
16+
17+
public function mount(Deck $deck): void
18+
{
19+
$this->deck = $deck;
20+
}
21+
22+
protected function rules(): array
1023
{
11-
return view('livewire.cards.create');
24+
return [
25+
'question' => [
26+
'required',
27+
'string',
28+
'max:255',
29+
Rule::unique('cards', 'question')->where('deck_id', $this->deck->id),
30+
],
31+
'answer' => [
32+
'required',
33+
'string',
34+
'max:255',
35+
],
36+
];
37+
}
38+
39+
public function store(): void
40+
{
41+
$validated = $this->validate();
42+
43+
$this->deck->cards()->create($validated);
44+
45+
$this->redirect(route('cards.index', $this->deck, absolute: false), navigate: true);
1246
}
1347
}

app/Livewire/Cards/Edit.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
namespace App\Livewire\Cards;
4+
5+
use App\Models\Card;
6+
use App\Models\Deck;
7+
use Illuminate\Validation\Rule;
8+
use Livewire\Component;
9+
10+
class Edit extends Component
11+
{
12+
public Deck $deck;
13+
14+
public Card $card;
15+
16+
public string $question = '';
17+
18+
public string $answer = '';
19+
20+
public function mount(Deck $deck, Card $card): void
21+
{
22+
if ($card->deck_id !== $deck->id) {
23+
abort(404);
24+
}
25+
26+
$this->deck = $deck;
27+
$this->card = $card;
28+
$this->question = $card->question;
29+
$this->answer = $card->answer;
30+
}
31+
32+
protected function rules(): array
33+
{
34+
return [
35+
'question' => [
36+
'required',
37+
'string',
38+
'max:255',
39+
Rule::unique('cards', 'question')->where('deck_id', $this->deck->id)->ignore($this->card->id),
40+
],
41+
'answer' => [
42+
'required',
43+
'string',
44+
'max:255',
45+
],
46+
];
47+
}
48+
49+
public function update(): void
50+
{
51+
$validated = $this->validate();
52+
53+
$this->card->update($validated);
54+
55+
$this->redirect(route('cards.index', $this->deck, absolute: false), navigate: true);
56+
}
57+
}

app/Livewire/Cards/Index.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace App\Livewire\Cards;
4+
5+
use App\Models\Card;
6+
use App\Models\Deck;
7+
use Illuminate\Auth\Access\AuthorizationException;
8+
use Illuminate\Contracts\Support\Renderable;
9+
use Livewire\Component;
10+
11+
class Index extends Component
12+
{
13+
public Deck $deck;
14+
15+
public function mount(Deck $deck): void
16+
{
17+
$this->deck = $deck;
18+
}
19+
20+
public function render(): Renderable
21+
{
22+
return view('livewire.cards.index', [
23+
'cards' => $this->deck->cards()->latest('id')->paginate(),
24+
]);
25+
}
26+
27+
/**
28+
* @throws AuthorizationException
29+
*/
30+
public function delete(Card $card): void
31+
{
32+
$this->authorize('delete', $card);
33+
34+
$card->delete();
35+
}
36+
}

app/Livewire/Decks/Index.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class Index extends Component
1414
public function render(): Renderable
1515
{
1616
return view('livewire.decks.index', [
17-
'decks' => Auth::user()->decks()->latest('id')->paginate(),
17+
'decks' => Auth::user()->decks()->latest('id')->withCount('cards')->paginate(),
1818
]);
1919
}
2020
}

app/Livewire/Decks/Show.php

Lines changed: 0 additions & 16 deletions
This file was deleted.

app/Models/Card.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace App\Models;
4+
5+
use Illuminate\Database\Eloquent\Factories\HasFactory;
6+
use Illuminate\Database\Eloquent\Model;
7+
use Illuminate\Database\Eloquent\Relations\BelongsTo;
8+
9+
class Card extends Model
10+
{
11+
/** @use HasFactory<\Database\Factories\CardFactory> */
12+
use HasFactory;
13+
14+
protected $fillable = ['question', 'answer'];
15+
16+
public function deck(): BelongsTo
17+
{
18+
return $this->belongsTo(Deck::class);
19+
}
20+
}

app/Models/Deck.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Illuminate\Database\Eloquent\Factories\HasFactory;
66
use Illuminate\Database\Eloquent\Model;
77
use Illuminate\Database\Eloquent\Relations\BelongsTo;
8+
use Illuminate\Database\Eloquent\Relations\HasMany;
89

910
class Deck extends Model
1011
{
@@ -17,4 +18,9 @@ public function user(): BelongsTo
1718
{
1819
return $this->belongsTo(User::class);
1920
}
21+
22+
public function cards(): HasMany
23+
{
24+
return $this->hasMany(Card::class);
25+
}
2026
}

app/Policies/CardPolicy.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace App\Policies;
4+
5+
use App\Models\Card;
6+
use App\Models\User;
7+
8+
class CardPolicy
9+
{
10+
public function create(User $user): bool
11+
{
12+
return true;
13+
}
14+
15+
public function update(User $user, Card $card): bool
16+
{
17+
return $user->id === $card->deck->user_id;
18+
}
19+
20+
public function delete(User $user, Card $card): bool
21+
{
22+
return $user->id === $card->deck->user_id;
23+
}
24+
}

database/factories/CardFactory.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Database\Factories;
4+
5+
use App\Models\Deck;
6+
use Illuminate\Database\Eloquent\Factories\Factory;
7+
8+
/**
9+
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Card>
10+
*/
11+
class CardFactory extends Factory
12+
{
13+
/**
14+
* Define the model's default state.
15+
*
16+
* @return array<string, mixed>
17+
*/
18+
public function definition(): array
19+
{
20+
return [
21+
'deck_id' => Deck::factory(),
22+
'question' => $this->faker->unique()->words(mt_rand(1, 3), true).'?',
23+
'answer' => $this->faker->words(mt_rand(1, 3), true),
24+
];
25+
}
26+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*/
12+
public function up(): void
13+
{
14+
Schema::create('cards', function (Blueprint $table) {
15+
$table->id();
16+
$table->foreignId('deck_id')->constrained();
17+
$table->string('question')->index();
18+
$table->string('answer');
19+
$table->timestamps();
20+
21+
$table->unique(['deck_id', 'question']);
22+
});
23+
}
24+
25+
/**
26+
* Reverse the migrations.
27+
*/
28+
public function down(): void
29+
{
30+
Schema::dropIfExists('cards');
31+
}
32+
};

0 commit comments

Comments
 (0)