
Миграция, модель и фабрика
Создадим файл миграции и модель для постов с помощью команды: php artisan make:model Post --migration.
Файл миграции будет иметь следующее содержимое:
<?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->string('title');
$table->text('body');
$table->boolean('published')->default(0);
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('posts');
}
}
Как можем видеть каждый пост будет состоять из заголовка - title, текста поста - body и флага (опубликован или нет пост) - published.
Файл модели будет иметь следующее содержимое:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
class Post extends Model
{
use HasFactory;
public static function getPosts($source)
{
switch ($source = 'bd') {
case 'bd':
return static::where('published', 1)->orderBy('id', 'desc')->take(10)->get();
break;
case 'cache':
return Cache::rememberForever(md5(__NAMESPACE__ . __CLASS__ . __METHOD__),
function() {
return static::where('published', 1)->orderBy('id', 'desc')->take(10)->get();
});
break;
}
}
public static function countAll($source)
{
switch ($source) {
case 'bd':
return static::count();
break;
case 'cache':
return Cache::rememberForever(md5(__NAMESPACE__ . __CLASS__ . __METHOD__),
function() {
return static::count();
});
break;
}
}
public static function countPublished($source)
{
switch ($source) {
case 'bd':
return static::where('published', 1)->count();
break;
case 'cache':
return Cache::rememberForever(md5(__NAMESPACE__ . __CLASS__ . __METHOD__),
function() {
return static::where('published', 1)->count();
});
break;
}
}
}
В модели 3 метода: getPosts($source), countAll($source) и countPublished($source). В параметре $source передается значение того, откуда мы ходим получить данные: из модели или из кэша.
- getPosts($source) - получаем список последних 10 опубликованных постов
- countAll($source) - получаем количество всех постов
- countPublished($source) - получаем количество опубликованных постов
Чтобы заполнить таблицу автоматически, создадим фабрику PostFactory с помощью команды: php artisan make:factory PostFactory.
Файл фабрики будет иметь следующее содержимое:
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class PostFactory extends Factory
{
public function definition()
{
return [
'title' => $this->faker->sentence,
'body' => $this->faker->paragraph,
'published' => $this->faker->numberBetween(0, 1),
];
}
}
Теперь заполним таблицу данными с помощью tinker. Запустим его с помощью команды: php artisan tinker.
Создать, например, 5000 постов можно командой (в Laravel 8): \App\Models\Post::factory(5000)->create();
Маршруты
Для работы примера в файле web.php (папка routes) добавим 2 дополнительные строки:
Route::get('/posts', [PostController::class, 'index'])->name('posts.index');
Route::get('/flush', [PostController::class, 'flush'])->name('flush');
Маршрут /posts выводит список 10 постов из модели, а маршрут /flush обнуляет содержимое кэша.
Контроллер
Теперь создадим контроллер, отвечающий за обработку этих маршрутов – PostController. Для этого используем следующую команду: php artisan make:controller PostController.
Файл контроллера будет иметь следующее содержимое:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Post;
use Illuminate\Support\Facades\Cache;
class PostController extends Controller
{
public function index(Request $request) {
$source = $request->get('source');
$time_start = microtime(true);
if ($source) {
switch ($source) {
case 'bd':
$posts = Post::getPosts('bd');
$numberAll = Post::countAll('bd');
$numberPublished = Post::countPublished('bd');
break;
case 'cache':
$posts = Post::getPosts('cache');
$numberAll = Post::countAll('cache');
$numberPublished = Post::countPublished('cache');
break;
}
} else {
$posts = Post::getPosts('bd');
$numberAll = Post::countAll('bd');
$numberPublished = Post::countPublished('bd');
}
$time_end = microtime(true);
$execution_time = round(($time_end - $time_start) * 1000, 2);
return view('index', compact('posts', 'numberAll', 'numberPublished', 'execution_time'));
}
public function flush() {
Cache::flush();
return redirect()->route('posts.index', 'source=bd&time='.microtime(true));
}
}
В файле контроллера у нас есть 2 метода index(Request $request) и flush(). С помощью первого мы получаем список из 10 постов, а с помощью второго чистим кэш. В первом методе в качестве параметра используем содержимое реквеста, чтобы определить откуда извлечь данные: из БД или из кэша. Время работы замеряем с помощью php-функции microtime().
Страница с постами
Нам нужна страница, на которую будет выводиться список постов. Для этого создадим новый вид – index.blade.php (папка resources/views). Его содержимое должно быть следующим:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
</head>
<body>
<div id="app" class="container">
<div class="text-center my-3">
<h1>Кэширование постов</h1>
<h3>
Всего постов: {{$numberAll}} | Опубликованных постов: {{$numberPublished}}<br>
Время в миллисекундах: {{$execution_time}}
</h3>
<a href="{{route('posts.index', 'source=bd&time='.microtime(true))}}" class="btn btn-primary">Загрузить из бд</a>
<a href="{{route('posts.index', 'source=cache&time='.microtime(true))}}" class="btn btn-primary">Загрузить из кэша</a>
<a href="{{route('flush', 'time='.microtime(true))}}" class="btn btn-secondary">Очистить кэш</a>
<p><small>Выборка последних 10 опубликованных постов</small></p>
</div>
<div class="row">
@foreach ($posts as $post)
<article class="card col-sm-8 offset-sm-2 p-3 my-1">
<h5>
<span class="badge bg-secondary">ID {{$post->id}}</span>
<span class="badge bg-success">Опубликован</span>
{{$post->title}}
</h5>
<body>
{{$post->body}}
</body>
</article>
@endforeach
</div>
</div>
</body>
</html>
На странице используем bootstrap. Также вы можете заметить, что в ссылках используется get-параметр time со значением php-функции microtime(), это необходимо, для того, чтобы сам браузер не кэшировал страницу, и можно было бы каждый раз делать запрос к БД или кэшу самого приложения, а не к кэшу браузера.
В видео (YouTube) можно наглядно посмотреть разницу в скорости извлечения данных из кэша и из БД на примере 50 000 записей.
Оставьте свой комментарий
Комментариев нет