Skip to content

Commit a9d542f

Browse files
committed
Create series
1 parent 45bf394 commit a9d542f

File tree

11 files changed

+267
-1
lines changed

11 files changed

+267
-1
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace App\Http\Controllers\Articles;
4+
5+
use App\Http\Controllers\Controller;
6+
use App\Http\Middleware\Authenticate;
7+
use App\Http\Middleware\RedirectIfUnconfirmed;
8+
use App\Http\Requests\SeriesRequest;
9+
use App\Jobs\CreateSeries;
10+
use App\Models\Tag;
11+
12+
class SeriesController extends Controller
13+
{
14+
public function __construct()
15+
{
16+
$this->middleware([Authenticate::class, RedirectIfUnconfirmed::class], ['except' => ['index', 'show']]);
17+
}
18+
19+
public function create()
20+
{
21+
$tags = Tag::all();
22+
$selectedTags = old('tags') ?: [];
23+
24+
return view('articles.series.create', ['tags' => $tags, 'selectedTags' => $selectedTags]);
25+
}
26+
27+
public function store(SeriesRequest $request)
28+
{
29+
$series = $this->dispatchNow(CreateSeries::fromRequest($request));
30+
31+
$this->success('series.created');
32+
33+
return redirect()->route('series.show', $series->id());
34+
}
35+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace App\Http\Requests;
4+
5+
use App\User;
6+
7+
class SeriesRequest extends Request
8+
{
9+
public function rules()
10+
{
11+
return [
12+
'title' => ['required', 'max:100'],
13+
'tags' => 'array',
14+
'tags.*' => 'exists:tags,id',
15+
];
16+
}
17+
18+
public function author(): User
19+
{
20+
return $this->user();
21+
}
22+
23+
public function title(): string
24+
{
25+
return $this->get('title');
26+
}
27+
28+
public function tags(): array
29+
{
30+
return $this->get('tags', []);
31+
}
32+
}

app/Jobs/CreateSeries.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
namespace App\Jobs;
4+
5+
use App\Http\Requests\SeriesRequest;
6+
use App\Models\Series;
7+
use App\User;
8+
9+
final class CreateSeries
10+
{
11+
/**
12+
* @var string
13+
*/
14+
private $title;
15+
16+
/**
17+
* @var \App\User
18+
*/
19+
private $author;
20+
21+
/**
22+
* @var array
23+
*/
24+
private $tags;
25+
26+
public function __construct(string $title, User $author, array $tags = [])
27+
{
28+
$this->title = $title;
29+
$this->author = $author;
30+
$this->tags = $tags;
31+
}
32+
33+
public static function fromRequest(SeriesRequest $request): self
34+
{
35+
return new static(
36+
$request->title(),
37+
$request->author(),
38+
$request->tags()
39+
);
40+
}
41+
42+
public function handle(): Series
43+
{
44+
$series = new Series([
45+
'title' => $this->title,
46+
]);
47+
$series->authoredBy($this->author);
48+
$series->syncTags($this->tags);
49+
$series->save();
50+
51+
return $series;
52+
}
53+
}

app/Models/Series.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
namespace App\Models;
44

55
use App\Helpers\HasAuthor;
6+
use App\Helpers\HasTags;
67
use Illuminate\Database\Eloquent\Model;
78

89
class Series extends Model
910
{
10-
use HasAuthor;
11+
use HasAuthor, HasTags;
1112

1213
/**
1314
* {@inheritdoc}
@@ -16,6 +17,16 @@ class Series extends Model
1617
'title',
1718
];
1819

20+
public function id(): int
21+
{
22+
return $this->id;
23+
}
24+
25+
public function title(): string
26+
{
27+
return $this->title;
28+
}
29+
1930
public function articles()
2031
{
2132
return $this->hasMany(Article::class);

resources/lang/en/series.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
return [
4+
5+
'created' => 'Series successfully created!',
6+
7+
];
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<form action="{{ route(...$route) }}" method="POST">
2+
@csrf
3+
@method($method ?? 'POST')
4+
5+
@formGroup('title')
6+
<label for="title">Title</label>
7+
<input
8+
type="text"
9+
name="title"
10+
id="title"
11+
value="{{ isset($series) ? $series->title() : null }}"
12+
class="form-control"
13+
required maxlength="60"
14+
/>
15+
<span class="text-gray-600 text-sm">Maximum 60 characters.</span>
16+
@error('title')
17+
@endFormGroup
18+
19+
@formGroup('tags')
20+
<label for="tags">Tags</label>
21+
22+
<select name="tags[]" id="create-series" multiple x-data="{}" x-init="function () { choices($el) }">
23+
@foreach($tags as $tag)
24+
<option value="{{ $tag->id }}" @if(in_array($tag->id, $selectedTags)) selected @endif>{{ $tag->name }}</option>
25+
@endforeach
26+
</select>
27+
28+
@error('tags')
29+
@endFormGroup
30+
31+
<div class="flex justify-end items-center">
32+
<a href="{{ isset($series) ? route('series.show', $series->id()) : route('series') }}" class="text-green-darker mr-4">Cancel</a>
33+
<button type="submit" class="button button-primary">{{ isset($series) ? 'Update series' : 'Create series' }}</button>
34+
</div>
35+
</form>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
@title('Create a series')
2+
3+
@extends('layouts.default')
4+
5+
@section('content')
6+
<div class="container mx-auto p-4 flex justify-center">
7+
<div class="w-full md:w-2/3 xl:w-1/2">
8+
<div class="md:p-4 md:border-2 md:rounded md:bg-gray-100">
9+
@include('articles.series._form', [
10+
'route' => ['series.store'],
11+
])
12+
</div>
13+
</div>
14+
</div>
15+
@endsection

routes/bindings.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,6 @@
2424
Route::bind('article', function (string $slug) {
2525
return App\Models\Article::findBySlug($slug);
2626
});
27+
Route::bind('series', function (string $id) {
28+
return App\Models\Series::findOrFail($id);
29+
});

routes/web.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@
7979

8080
// Articles
8181
Route::prefix('articles')->namespace('Articles')->group(function () {
82+
// Series
83+
Route::prefix('series')->group(function () {
84+
Route::get('/create', 'SeriesController@create')->name('series.create');
85+
Route::post('/', 'SeriesController@store')->name('series.store');
86+
});
87+
8288
Route::get('/', 'ArticlesController@index')->name('articles');
8389
Route::get('/create', 'ArticlesController@create')->name('articles.create');
8490
Route::post('/', 'ArticlesController@store')->name('articles.store');

tests/Feature/SeriesTest.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace Tests\Feature;
4+
5+
use App\Models\Article;
6+
use App\Models\Tag;
7+
use Illuminate\Foundation\Testing\DatabaseMigrations;
8+
9+
class SeriesTest extends BrowserKitTestCase
10+
{
11+
use DatabaseMigrations;
12+
13+
/** @test */
14+
public function users_cannot_create_a_series_when_not_logged_in()
15+
{
16+
$this->visit('/articles/series/create')
17+
->seePageIs('/login');
18+
}
19+
20+
/** @test */
21+
public function users_can_create_a_series()
22+
{
23+
$tag = factory(Tag::class)->create(['name' => 'Test Tag']);
24+
25+
$this->login();
26+
27+
$this->post('/articles/series', [
28+
'title' => 'A developers guide to SVG',
29+
'tags' => [$tag->id()],
30+
])
31+
->assertRedirectedTo('/articles/series/1')
32+
->assertSessionHas('success', 'Series successfully created!');
33+
}
34+
35+
/** @test */
36+
public function users_cannot_create_a_series_with_a_title_that_is_too_long()
37+
{
38+
$this->login();
39+
40+
$response = $this->post('/articles/series', [
41+
'title' => 'A developers guide to SVG which is an image format written in XML which is highly customisable and flexible',
42+
]);
43+
44+
$response->assertSessionHas('error', 'Something went wrong. Please review the fields below.');
45+
$response->assertSessionHasErrors(['title' => 'The title may not be greater than 100 characters.']);
46+
}
47+
}

0 commit comments

Comments
 (0)