# 1. Create Gallery thumbnail for post select (ok)

Source code

{% file src="https://1592565776-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LVfVRgKBlsVnWvhnDaa%2Fuploads%2FD8L7p2aaFxQSdcuwnAgp%2Flva6.zip?alt=media&token=85e3c5f3-2d6f-4100-80f4-698c6787ef07" %}

<figure><img src="https://1592565776-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LVfVRgKBlsVnWvhnDaa%2Fuploads%2Fcly2kNJ1G5wqdYPZKXVi%2Fimage.png?alt=media&#x26;token=a953ab5d-3904-43a1-973d-13db09a5fa4e" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1592565776-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LVfVRgKBlsVnWvhnDaa%2Fuploads%2F2TUMoSrkpsQdRnrBSEyL%2Fimage.png?alt=media&#x26;token=37934ab0-193a-4d55-85cc-02ad2c84b092" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1592565776-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LVfVRgKBlsVnWvhnDaa%2Fuploads%2F0dvjHX6T4laTlCJdJtg9%2Fimage.png?alt=media&#x26;token=47f540f7-e34a-4ddd-bb9c-c63d5d56fffb" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1592565776-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LVfVRgKBlsVnWvhnDaa%2Fuploads%2FmR2UpGdBSetZy4P0ioF5%2Fimage.png?alt=media&#x26;token=a63b63c4-0a86-492b-90df-ea56aa7ec409" alt=""><figcaption></figcaption></figure>

<figure><img src="https://1592565776-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LVfVRgKBlsVnWvhnDaa%2Fuploads%2FynCdYRewGTQJEMYvjl4k%2Fimage.png?alt=media&#x26;token=27a1bbdc-0b27-4cf1-ab93-2778cecc7ae3" alt=""><figcaption></figcaption></figure>

C:\xampp82\htdocs\lva6\app\Models\Media.php

```php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Spatie\MediaLibrary\MediaCollections\Models\Media as BaseMedia;
class Media extends BaseMedia
{
  use HasFactory;
  /**
   * The attributes that should be mutated to dates.
   *
   * @var array
   */
  protected $dates = [
    'posted_at'
  ];
}

```

C:\xampp82\htdocs\lva6\app\Models\MediaLibrary.php

```php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
class MediaLibrary extends Model implements HasMedia
{
  use InteractsWithMedia;
  public function registerMediaConversions(Media $media = null): void
  {
    $this->addMediaConversion('thumb')
      ->width(350)
      ->height(250);
  }
}

```

C:\xampp82\htdocs\lva6\app\Models\Post.php

```php
<?php
namespace App\Models;
use App\Scopes\PostedScope;
use DateTimeInterface;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Str;
class Post extends Model
{
  use HasFactory;
  /**
   * The attributes that are mass assignable.
   *
   * @var array
   */
  protected $fillable = [
    'author_id',
    'title',
    'content',
    'posted_at',
    'slug',
    'thumbnail_id',
  ];
  /**
   * The attributes that should be mutated to dates.
   *
   * @var array
   */
  protected $dates = [
    'posted_at'
  ];
  /**
   * The "booting" method of the model.
   */
  protected static function boot(): void
  {
    parent::boot();
    static::addGlobalScope(new PostedScope);
  }
  /**
   * Prepare a date for array / JSON serialization.
   */
  protected function serializeDate(DateTimeInterface $date): string
  {
    return $date->format('Y-m-d H:i:s');
  }
  /**
   * Get the route key for the model.
   */
  public function getRouteKeyName(): string
  {
    if (request()->expectsJson()) {
      return 'id';
    }
    return 'slug';
  }
  /**
   * Scope a query to search posts
   */
  public function scopeSearch(Builder $query, ?string $search)
  {
    if ($search) {
      return $query->where('title', 'LIKE', "%{$search}%");
    }
  }
  /**
   * Scope a query to order posts by latest posted
   */
  public function scopeLatest(Builder $query): Builder
  {
    return $query->orderBy('posted_at', 'desc');
  }
  /**
   * Scope a query to only include posts posted last month.
   */
  public function scopeLastMonth(Builder $query, int $limit = 5): Builder
  {
    return $query->whereBetween('posted_at', [carbon('1 month ago'), now()])
      ->latest()
      ->limit($limit);
  }
  /**
   * Scope a query to only include posts posted last week.
   */
  public function scopeLastWeek(Builder $query): Builder
  {
    return $query->whereBetween('posted_at', [carbon('1 week ago'), now()])
      ->latest();
  }
  /**
   * Return the post's author
   */
  public function author(): BelongsTo
  {
    return $this->belongsTo(User::class, 'author_id');
  }
  /**
   * Return the post's thumbnail
   */
  public function thumbnail(): BelongsTo
  {
    return $this->belongsTo(Media::class);
  }
  /**
   * return the excerpt of the post content
   */
  public function excerpt(int $length = 50): string
  {
    return Str::limit($this->content, $length);
  }
  /**
   * return true if the post has a thumbnail
   */
  public function hasThumbnail(): bool
  {
    return filled($this->thumbnail_id);
  }
}

```

C:\xampp82\htdocs\lva6\app\Models\User.php

```php
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\belongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Illuminate\Support\Str;
class User extends Authenticatable
{
  use HasApiTokens, HasFactory, Notifiable;
  /**
   * The attributes that are mass assignable.
   *
   * @var array<int, string>
   */
  protected $fillable = [
    'name',
    'email',
    'password',
  ];
  /**
   * The attributes that should be hidden for serialization.
   *
   * @var array<int, string>
   */
  protected $hidden = [
    'password',
    'remember_token',
  ];
  /**
   * The attributes that should be cast.
   *
   * @var array<string, string>
   */
  protected $casts = [
    'email_verified_at' => 'datetime',
  ];
  /**
   * Get the user's fullname titleized.
   */
  public function getFullnameAttribute(): string
  {
    return Str::title($this->name);
  }
  /**
   * Scope a query to only include users registered last week.
   */
  public function scopeLastWeek(Builder $query): Builder
  {
    return $query->whereBetween('registered_at', [carbon('1 week ago'), now()])
      ->latest();
  }
  /**
   * Scope a query to order users by latest registered.
   */
  public function scopeLatest(Builder $query): Builder
  {
    return $query->orderBy('registered_at', 'desc');
  }

  /**
   * Check if the user can be an author
   */
  public function canBeAuthor(): bool
  {
    return $this->isAdmin() || $this->isEditor();
  }

  /**
   * Check if the user has role admin
   */
  public function isAdmin(): bool
  {
    // return $this->hasRole(Role::ROLE_ADMIN);
  }
  /**
   * Check if the user has role editor
   */
  public function isEditor(): bool
  {
    // return $this->hasRole(Role::ROLE_EDITOR);
  }
  /**
   * Return the user's posts
   */
  public function posts(): HasMany
  {
    return $this->hasMany(Post::class, 'author_id');
  }
  /**
   * Return the user's comments
   */
  public function comments(): HasMany
  {
    return $this->hasMany(Comment::class, 'author_id');
  }
}

```

C:\xampp82\htdocs\lva6\app\Observers\MediaObserver.php

Hiện tại posted\_at chưa hoạt động :( không hiểu tại sao? phải đặt posted\_at dưới dạng timestamp giá trị mặc định :(

```php
<?php
namespace App\Observers;
use App\Models\Media;
class MediaObserver
{
  /**
   * Listen to the Media creating event.
   */
  public function creating(Media $medium): void
  {
    $medium->posted_at = now();
  }
}

```

C:\xampp82\htdocs\lva6\app\Observers\PostObserver.php

```php
<?php
namespace App\Observers;
use App\Models\Post;
use Illuminate\Support\Str;
class PostObserver
{
  /**
   * Listen to the Post saving event.
   */
  public function saving(Post $post): void
  {
    $post->slug = Str::slug($post->title, '-');
  }
}

```

C:\xampp82\htdocs\lva6\app\Providers\ObserverServiceProvider.php

```php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Models\Media;
use App\Models\Post;
use App\Observers\MediaObserver;
use App\Observers\PostObserver;
class ObserverServiceProvider extends ServiceProvider
{
  /**
   * Bootstrap the application services.
   */
  public function boot(): void
  {
    Post::observe(PostObserver::class);
    Media::observe(MediaObserver::class);
  }
}

```

C:\xampp82\htdocs\lva6\app\Scopes\PostedScope.php

```php
<?php
namespace App\Scopes;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Support\Facades\Auth;
class PostedScope implements Scope
{
  /**
   * Apply the scope to a given Eloquent query builder.
   */
  public function apply(Builder $builder, Model $model): void
  {
    $user = Auth::user() ?? Auth::guard('api')->user();
    // if not connected or if connected but not admin
    if (!$user) {
      $builder->where('posted_at', '<=', now());
    }
  }
}

```

C:\xampp82\htdocs\lva6\app\Providers\RouteServiceProvider.php

```php
<?php
namespace App\Providers;
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;
class RouteServiceProvider extends ServiceProvider
{
  /**
   * The path to the "home" route for your application.
   *
   * Typically, users are redirected here after authentication.
   *
   * @var string
   */
  public const HOME = '/home';
  /**
   * Define your route model bindings, pattern filters, and other route configuration.
   */
  public function boot(): void
  {
    parent::boot();
    $this->configureRateLimiting();
    // $this->routes(function () {
    //   Route::middleware('api')
    //     ->prefix('api')
    //     ->group(base_path('routes/api.php'));
    //   Route::middleware('web')
    //     ->group(base_path('routes/web.php'));
    // });
  }
  /**
   * Define the routes for the application.
   */
  public function map(): void
  {
    $this->mapApiRoutes();
    $this->mapAuthRoutes();
    $this->mapWebRoutes();
    $this->mapAdminRoutes();
  }
  /**
   * Define the "api" routes for the application.
   *
   * These routes are typically stateless.
   */
  protected function mapApiRoutes(): void
  {
    Route::prefix('api')
      ->middleware('api')
      ->group(base_path('routes/api.php'));
  }
  /**
   * Define the "auth" routes for the application.
   *
   * These routes are typically stateless.
   */
  protected function mapAuthRoutes(): void
  {
    Route::middleware('web')
      ->group(base_path('routes/auth.php'));
  }
  /**
   * Define the "web" routes for the application.
   *
   * These routes all receive session state, CSRF protection, etc.
   */
  protected function mapWebRoutes(): void
  {
    Route::middleware('web')
      ->group(base_path('routes/web.php'));
  }
  /**
   * Define the "admin" routes for the application.
   *
   * These routes are typically stateless.
   */
  protected function mapAdminRoutes(): void
  {
    Route::prefix('admin')
      ->middleware(['web', 'auth'])
      ->as('admin.')
      ->group(base_path('routes/admin.php'));
  }
  /**
   * Configure the rate limiters for the application.
   */
  protected function configureRateLimiting(): void
  {
    RateLimiter::for('api', function (Request $request) {
      return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
    });
  }
}

```

C:\xampp82\htdocs\lva6\config\app.php

```php
App\Providers\RouteServiceProvider::class,
App\Providers\ObserverServiceProvider::class,
```

C:\xampp82\htdocs\lva6\database\migrations\2016\_12\_19\_080506\_create\_posts\_table.php

```php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
  /**
   * Run the migrations.
   *
   * @return void
   */
  public function up()
  {
    Schema::create('posts', function (Blueprint $table) {
      $table->increments('id');
      $table->integer('author_id')->unsigned()->default(0);
      $table->string('title')->nullable();
      $table->string('slug')->unique();
      $table->integer('thumbnail_id')->unsigned()->nullable();
      $table->text('content')->nullable();
      $table->timestamp('posted_at')->nullable();
      $table->timestamps();
    });
  }
  /**
   * Reverse the migrations.
   *
   * @return void
   */
  public function down()
  {
    Schema::drop('posts');
  }
};

```

C:\xampp82\htdocs\lva6\database\migrations\2023\_10\_28\_092220\_create\_media\_libraries\_table.php

```php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
  /**
   * Run the migrations.
   */
  public function up(): void
  {
    Schema::create('media_libraries', function (Blueprint $table) {
      $table->id();
      $table->timestamps();
    });
  }
  /**
   * Reverse the migrations.
   */
  public function down(): void
  {
    Schema::dropIfExists('media_libraries');
  }
};

```

C:\xampp82\htdocs\lva6\database\migrations\2023\_10\_30\_090733\_create\_media\_table.php

```php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
  public function up(): void
  {
    Schema::create('media', function (Blueprint $table) {
      $table->id();
      $table->morphs('model');
      $table->uuid('uuid')->nullable()->unique();
      $table->string('collection_name');
      $table->string('name');
      $table->string('file_name');
      $table->string('mime_type')->nullable();
      $table->string('disk');
      $table->string('conversions_disk')->nullable();
      $table->unsignedBigInteger('size');
      $table->json('manipulations');
      $table->json('custom_properties');
      $table->json('generated_conversions');
      $table->json('responsive_images');
      $table->timestamp('posted_at');
      $table->unsignedInteger('order_column')->nullable()->index();
      $table->nullableTimestamps();
    });
  }
  /**
   * Reverse the migrations.
   */
  public function down(): void
  {
    Schema::dropIfExists('media');
  }
};

```

C:\xampp82\htdocs\lva6\database\seeders\DatabaseSeeder.php

```php
<?php
namespace Database\Seeders;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\MediaLibrary;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
class DatabaseSeeder extends Seeder
{
  /**
   * Seed the application's database.
   */
  public function run(): void
  {
    // MediaLibrary
    MediaLibrary::firstOrCreate([]);
    // Users
    $user = User::firstOrCreate(
      ['email' => 'test1@gmail.com'],
      [
        'name' => 'test1',
        'password' => Hash::make('12345678'),
        'email_verified_at' => now()
      ]
    );
  }
}

```

C:\xampp82\htdocs\lva6\routes\web.php

```php
<?php
use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "web" middleware group. Make something great!
|
*/
Route::get('/', function () {
    return view('welcome');
});
Auth::routes();
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
Route::resource('posts', PostController::class)->only('show');

```
