Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for posting content via a REST API #793

Merged
merged 15 commits into from
Jan 8, 2022
22 changes: 15 additions & 7 deletions app/Http/Controllers/Articles/ArticlesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use App\Http\Controllers\Controller;
use App\Http\Middleware\Authenticate;
use App\Http\Requests\ArticleRequest;
use App\Http\Resources\ArticleResource;
use App\Jobs\CreateArticle;
use App\Jobs\DeleteArticle;
use App\Jobs\UpdateArticle;
Expand All @@ -17,6 +18,7 @@
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Symfony\Component\HttpFoundation\Response;

class ArticlesController extends Controller
{
Expand Down Expand Up @@ -101,11 +103,13 @@ public function create()

public function store(ArticleRequest $request)
{
$article = $this->dispatchNow(CreateArticle::fromRequest($request));
$article = $this->dispatchSync(CreateArticle::fromRequest($request));

$this->success($request->shouldBeSubmitted() ? 'articles.submitted' : 'articles.created');

return redirect()->route('articles.show', $article->slug());
return $request->wantsJson()
? ArticleResource::make($article)
: redirect()->route('articles.show', $article->slug());
}

public function edit(Article $article)
Expand All @@ -125,25 +129,29 @@ public function update(ArticleRequest $request, Article $article)

$wasNotPreviouslySubmitted = $article->isNotSubmitted();

$article = $this->dispatchNow(UpdateArticle::fromRequest($article, $request));
$article = $this->dispatchSync(UpdateArticle::fromRequest($article, $request));

if ($wasNotPreviouslySubmitted && $request->shouldBeSubmitted()) {
$this->success('articles.submitted');
} else {
$this->success('articles.updated');
}

return redirect()->route('articles.show', $article->slug());
return $request->wantsJson()
? ArticleResource::make($article)
: redirect()->route('articles.show', $article->slug());
}

public function delete(Article $article)
public function delete(Request $request, Article $article)
{
$this->authorize(ArticlePolicy::DELETE, $article);

$this->dispatchNow(new DeleteArticle($article));
$this->dispatchSync(new DeleteArticle($article));

$this->success('articles.deleted');

return redirect()->route('articles');
return $request->wantsJson()
? response()->json([], Response::HTTP_NO_CONTENT)
: redirect()->route('articles');
}
}
33 changes: 33 additions & 0 deletions app/Http/Controllers/Settings/ApiTokenController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace App\Http\Controllers\Settings;

use App\Http\Controllers\Controller;
use App\Http\Requests\CreateApiTokenRequest;
use App\Http\Requests\DeleteApiTokenRequest;
use App\Jobs\CreateApiToken;
use App\Jobs\DeleteApiToken;
use Illuminate\Auth\Middleware\Authenticate;
use Illuminate\Support\Facades\Auth;

class ApiTokenController extends Controller
{
public function __construct()
{
$this->middleware(Authenticate::class);
}

public function store(CreateApiTokenRequest $request)
{
$this->dispatchSync(new CreateApiToken(Auth::user(), $request->name()));

return redirect()->route('settings.profile');
}

public function destroy(DeleteApiTokenRequest $request)
{
$this->dispatchSync(new DeleteApiToken(Auth::user(), $request->id()));

return redirect()->route('settings.profile');
}
}
20 changes: 20 additions & 0 deletions app/Http/Requests/CreateApiTokenRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class CreateApiTokenRequest extends FormRequest
{
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:255'],
];
}

public function name(): string
{
return (string) $this->get('name');
}
}
20 changes: 20 additions & 0 deletions app/Http/Requests/DeleteApiTokenRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class DeleteApiTokenRequest extends FormRequest
{
public function rules(): array
{
return [
'id' => ['required', 'exists:personal_access_tokens,id'],
];
}

public function id(): string
{
return (string) $this->get('id');
}
}
28 changes: 28 additions & 0 deletions app/Http/Resources/ArticleResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

/**
* @mixin \App\Models\Article
*/
class ArticleResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->getKey(),
'url' => route('articles.show', $this->slug()),
'title' => $this->title(),
'body' => $this->body(),
'original_url' => $this->originalUrl(),
'author' => AuthorResource::make($this->author()),
'tags' => TagResource::collection($this->tags()),
'is_submitted' => $this->isSubmitted(),
'submitted_at' => $this->submittedAt(),
'created_at' => $this->createdAt(),
'updated_at' => $this->updatedAt(),
];
}
}
24 changes: 24 additions & 0 deletions app/Http/Resources/AuthorResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

/**
* @mixin \App\Models\User
*/
class AuthorResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->getKey(),
'email' => $this->emailAddress(),
'username' => $this->username(),
'name' => $this->name(),
'bio' => $this->bio(),
'twitter_handle' => $this->twitter(),
'github_username' => $this->githubUsername(),
];
}
}
20 changes: 20 additions & 0 deletions app/Http/Resources/TagResource.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

/**
* @mixin \App\Models\Tag
*/
class TagResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->getKey(),
'name' => $this->name(),
'slug' => $this->slug(),
];
}
}
24 changes: 24 additions & 0 deletions app/Jobs/CreateApiToken.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace App\Jobs;

use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class CreateApiToken implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

public function __construct(private User $user, private string $name)
{
}

public function handle(): void
{
$this->user->createToken($this->name);
}
}
24 changes: 24 additions & 0 deletions app/Jobs/DeleteApiToken.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace App\Jobs;

use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class DeleteApiToken implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

public function __construct(private User $user, private int $tokenId)
{
}

public function handle(): void
{
$this->user->tokens()->where('id', $this->tokenId)->delete();
}
}
2 changes: 2 additions & 0 deletions app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Auth;
use Laravel\Sanctum\HasApiTokens;

final class User extends Authenticatable implements MustVerifyEmail
{
use HasApiTokens;
use HasFactory;
use HasTimestamps;
use Notifiable;
Expand Down
3 changes: 2 additions & 1 deletion app/Providers/RouteServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;

Expand Down Expand Up @@ -49,7 +50,7 @@ public function boot()
protected function configureRateLimiting()
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60);
return Limit::perMinute(6);
driesvints marked this conversation as resolved.
Show resolved Hide resolved
});
}
}
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"laravel-notification-channels/twitter": "^5.0",
"laravel/framework": "8.75.0",
"laravel/horizon": "^5.2",
"laravel/sanctum": "^2.13",
"laravel/slack-notification-channel": "^2.3",
"laravel/socialite": "^5.0",
"laravel/ui": "^3.0",
Expand Down
66 changes: 65 additions & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading