-
Notifications
You must be signed in to change notification settings - Fork 0
2. A Post Model Example
Lets take a look at an example config file and break down how it works.
$ php artisan scaffold:config Post After filling out the prompts, we are left with the following file at scaffolding/Post.php
<?php
return [
'name' => 'Post',
'order' => 0,
'fields' => [
'title' => ['type' => 'string', 'nullable' => false],
'body' => ['type' => 'text', 'nullable' => false],
'published_at' => ['type' => 'timestamp', 'nullable' => true],
'is_published' => ['type' => 'boolean', 'nullable' => false],
],
'relationships' => [
'User' => ['type' => 'belongsTo'],
'Comment' => ['type' => 'hasMany'],
'Tag' => ['type' => 'belongsToMany'],
],
'scaffolds' => [
'model' => true,
'migration' => true,
'controller' => true,
'resources' => true,
'requests' => true,
'policy' => true,
'factory' => true,
],
];Let's break this down by key:
The name of the model - will be the exact name you supplied to the command and used through the scaffolding
The order in which to publish the files - this is mostly due to migrations wanting to be run in a certain order. The script intentionally waits at least a second between models so that migrations do not have the same timestamp.
The fields that the model contains. Additional info includes what the field type is as well as whether or not they can be nullable. This information is used for almost every file in the process including: attribute casting, adding the fields to the fillable array, request validation and resources, and model factories.
Relationships to other models.
If it is a through relationship, you may specify the other model through which it is related.
If it is a belongsToMany relationship and you know it will be many-to-many, you may add a pivot table. Only add the pivot table to ONE of the models, whichever is migrated second. The scaffolders are smart and will create these tables and relationships with the correct naming.
These relationships will be added to models, controllers, resources, and factories.
The scaffolds you want this process to create. Note that if you create a controller, you will also create a policy, requests, and resources.
$ php artisan scaffold:application For now, this is the only file we have. I would recommend adding every model you know the application will have before running this command. Nevertheless, these are the files that are created based on the Post.php config above.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = ['title', 'body', 'published_at', 'is_published'];
protected $casts = [
'title' => 'string',
'body' => 'string',
'published_at' => 'timestamp',
'is_published' => 'boolean',
];
public function user()
{
return $this->belongsTo(User::class);
}
public function comments()
{
return $this->hasMany(Comment::class);
}
public function tags()
{
return $this->belongsToMany(Tag::class);
}
}<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePostsTable extends Migration
{
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->string('title');
$table->text('body');
$table->timestamp('published_at')->nullable();
$table->boolean('is_published')->default(0);
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('posts');
}
}<?php
namespace App\Http\Controllers;
use App\Http\Requests\PostStoreRequest;
use App\Http\Requests\PostUpdateRequest;
use App\Http\Resources\PostCollection;
use App\Http\Resources\PostResource;
use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Spatie\QueryBuilder\AllowedFilter;
use Spatie\QueryBuilder\QueryBuilder;
class PostController extends Controller
{
public function __construct()
{
$this->authorizeResource(Post::class);
}
public function index(Request $request)
{
$posts = QueryBuilder::for(Post::class)
->where('user_id', $request->user()->id)
->allowedFields(['id', 'title', 'body', 'published_at', 'is_published'])
->allowedIncludes(['user', 'comments', 'tags'])
->allowedFilters([
AllowedFilter::exact('user_id'), 'title', 'body', 'published_at', AllowedFilter::exact('is_published'),
])
->allowedSorts(['title', 'body', 'published_at', 'is_published'])
->jsonPaginate();
return new PostCollection($posts);
}
public function store(PostStoreRequest $request)
{
$post = new Post();
$post->user()->associate($request->user());
$post->fill($request->validated())->save();
return (new PostResource($post))->response()->setStatusCode(Response::HTTP_CREATED);
}
public function show(Post $post)
{
return (new PostResource($post))->response()->setStatusCode(Response::HTTP_OK);
}
public function update(PostUpdateRequest $request, Post $post)
{
$post->update($request->validated());
return (new PostResource($post))->response()->setStatusCode(Response::HTTP_OK);
}
public function destroy(Post $post)
{
$post->delete();
return response(null, Response::HTTP_NO_CONTENT);
}
}<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class PostResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'body' => $this->body,
'published_at' => $this->published_at,
'is_published' => $this->is_published,
'user' => new UserResource($this->whenLoaded('user')),
'comments' => new CommentCollection($this->whenLoaded('comments')),
'tags' => new TagCollection($this->whenLoaded('tags')),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
];
}
}<?php
namespace App\Http\Resources;
use App\Models\Post;
use Illuminate\Http\Resources\Json\ResourceCollection;
class PostCollection extends ResourceCollection
{
public function toArray($request)
{
return $this->collection->transform(function (Post $post) {
if ($post->relationLoaded('user')) {
$post['user'] = new UserResource($post->user);
}
if ($post->relationLoaded('comments')) {
$post['comments'] = new CommentCollection($post->comments);
}
if ($post->relationLoaded('tags')) {
$post['tags'] = new TagCollection($post->tags);
}
return $post->getAttributes();
});
}
}
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class PostStoreRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'title' => 'required|string',
'body' => 'required|string',
'published_at' => 'nullable',
'is_published' => 'required|boolean',
];
}
}<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class PostUpdateRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return ['title' => 'string', 'body' => 'string', 'published_at' => 'nullable', 'is_published' => 'boolean'];
}
}<?php
namespace App\Policies;
use App\Models\Post;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Auth\Access\Response;
class PostPolicy
{
use HandlesAuthorization;
public function viewAny(?User $user)
{
return true;
}
public function view(?User $user, Post $post)
{
return true;
}
public function create(User $user)
{
return true;
}
public function update(User $user, Post $post)
{
return $user->is($post->user) ? Response::allow() : Response::deny('You do not own this post.');
}
public function delete(User $user, Post $post)
{
return $user->is($post->user) ? Response::allow() : Response::deny('You do not own this post.');
}
public function restore(User $user, Post $post)
{
return $user->is($post->user) ? Response::allow() : Response::deny('You do not own this post.');
}
public function forceDelete(User $user, Post $post)
{
return $user->is($post->user) ? Response::allow() : Response::deny('You do not own this post.');
}
}
<?php
namespace Database\Factories;
use App\Models\Comment;
use App\Models\Post;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
class PostFactory extends Factory
{
protected $model = Post::class;
public function definition()
{
return [
'user_id' => optional(User::inRandomOrder()->first())->id ?? User::factory(),
'title' => $this->faker->sentence,
'body' => $this->faker->paragraphs(5, true),
'published_at' => $this->faker->boolean(50) ? $this->faker->dateTimeBetween($startDate = '-5 years', $endDate = 'now', 'UTC')->format('Y-m-d H:i:s') : null,
'is_published' => $this->faker->boolean(50),
];
}
public function configure()
{
return $this->afterCreating(function (Post $post) {
$post->comments()->saveMany(Comment::factory()->count(mt_rand(1, 10))->create());
});
}
}