Vlastní RSS feed úplně od začátku
Každý blog by měl mít vlastní RSS feed. V tomto článku si úplně od začátku vytvoříme vlastní RSS feed a ukážeme si, že pomocí balíčku od Spatie to lze provést velmi jednoduše a rychle. Pro naše účely využijeme balíček spatie/laravel-feed a celý projekt budeme stavět na Laravelu 8.
Příprava
Jako první si vytvoříme úplně nový Laravel projekt pomocí příkazu:
laravel new rss-project
Dále si nainstalujeme composer balíček spatie/laravel-feed
a zkopírujeme si jeho konfigurační soubory do našeho projektu:
composer require spatie/laravel-feed
php artisan vendor:publish --provider="Spatie\Feed\FeedServiceProvider" --tag="config"
Modelování dat
Vytvoříme si pár testovacích dat pro model. Náš model budou články třeba na blog a RSS bude obsahovat kolekci těchto článků. Jako první si vytvoříme model společně s migrací a faktorkou pro vygenerování testovacích dat:
php artisan make:model Post -mf
Model created successfully.
Factory created successfully.
Created Migration: 2020_10_08_043318_create_posts_table
V migraci si definujeme velmi jednoduchou strukturu:
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->foreignId('user_id');
$table->timestamps();
});
}
Pro naše účely nám kromě ID a časových razítek stačí název článku, jeho obsah a autor. Autora článku si definujeme pomocí cizího klíče, kdy výchozí migrace čerstvého Laravel projektu již obsahují vytvoření tabulky User
.
Dále si otevřeme soubor database/factories/PostFactory.php
, který obsahuje definování našich testovacích dat pro seedování. Laravel 8 faktorky již generuje ve třídě, proto její obsah bude vypadat takto:
class PostFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Post::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'title' => $this->faker->sentence,
'content' => $this->faker->paragraph(),
'user_id' => User::factory()
];
}
}
V metodě definition()
si nadefinujeme pomocí fakeru
jednoduché dummy data. Budeme předpokládat, že každý článek napsal někdo jiný a tedy při každém vytvoření článku se vytvoří i jeho autor.
Dále si vytvoříme samostatný seeder, který bude využívat faktorku pro naseedování dat:
php artisan make:seeder PostsSeeder
Ve vytvořené třídě Database\Seeder\PostsSeeder
si vytvoříme např. 50 nových článků a jejich autorů:
<?php
namespace Database\Seeders;
use App\Models\Post;
use Illuminate\Database\Seeder;
class PostsSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Post::factory()->times(50)->create();
}
}
Jako poslední pro vygenerování dat nám chybí zaregistrování tohoto seederu do hlavní třídy DatabaseSeeder
:
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
$this->call(PostsSeeder::class);
}
}
A nakonec spustíme migrace (předpokládám, že jste si již správně nastavili připojení k databázi). Společně s migracemi si necháme vygenerovat i data:
php artisan migrate:fresh --seed
Dropped all tables successfully.
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (61.27ms)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (40.83ms)
Migrating: 2019_08_19_000000_create_failed_jobs_table
Migrated: 2019_08_19_000000_create_failed_jobs_table (37.86ms)
Migrating: 2020_10_08_043318_create_posts_table
Migrated: 2020_10_08_043318_create_posts_table (28.55ms)
Seeding: Database\Seeders\PostsSeeder
Seeded: Database\Seeders\PostsSeeder (165.05ms)
Database seeding completed successfully.
Nastavení modelu
Abychom mohli využívat composer balíčku spatie/laravel-feed
, musíme prvně implementovat interface Feedable
v našem novém modelu Post
. Díky tomuto rozhraní musíme implementovat i další dvě metody (ve skutečnosti pouze jednu, ale bez druhé to fungovat nebude):
-
toFeedItem()
pro vyvoření jednotlivých položek do samotného RSS feedu -
getFeedItems()
- statická metoda pro získání všech RSS feed položek
Jako první si přidáme metodu toFeedItem()
:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Spatie\Feed\Feedable;
use Spatie\Feed\FeedItem;
class Post extends Model implements Feedable
{
use HasFactory;
public function toFeedItem(): FeedItem
{
return FeedItem::create()
->id($this->id)
->title($this->title)
->summary($this->content)
->updated($this->updated_at)
->link($this->link)
->author($this->user->name);
}
}
Metoda je pomerně pochopitelná, nicméně stále nefunkční. Jako první je potřeba získat data pro autora. To uděláme jednoduše pomocí definování vztahu mezi článkem (tabulka posts
) a uživatelem (tabulka users
):
public function user()
{
return $this->belongsTo(User::class);
}
Další atribut, který nám chybí vyřešit, je link
. Ten aktuálně nikde neexistuje a musíme si jej v modelu sami vytvořit. Využijeme implicitního routování a automatického navázání modelu, takže se nám vygeneruje odkaz na článek:
public function getLinkAttribute()
{
return route('posts.show', $this);
}
Jako poslední zbývá do modelu doplnit statickou metodu getFeedItems()
, která vrací kolekci dat, které jsou jednotlivě převedeny do instance FeedItem
:
public static function getFeedItems()
{
return static::all();
}
Routy a controller
Jako poslední nám zbývá vytvoření controlleru a cest tak, aby nám route('posts.show', $this)
fungovalo správně. Jako první si vygeneruje controller:
php artisan make:controller PostsController
Controller created successfully.
V nově vytvořeném PostController
si vytvoříme novou metodu show()
, která model kvůli zjednodušení vrací v JSON odpovědi:
<?php
namespace App\Http\Controllers;
use App\Models\Post;
class PostsController extends Controller
{
public function show(Post $post): Post
{
return $post;
}
}
Dále si potřebujeme upravit konfigurační soubor config/feed.php
, který jsme si dříve vygenerovali. Nejdůležitější je zde si upravit cestu k položkám:
<?php
return [
'feeds' => [
'main' => [
/*
* Here you can specify which class and method will return
* the items that should appear in the feed. For example:
* 'App\Model@getAllFeedItems'
*
* You can also pass an argument to that method:
* ['App\Model@getAllFeedItems', 'argument']
*/
'items' => 'App\Models\Post@getFeedItems',
/*
* The feed will be available on this url.
*/
'url' => '/posts.rss',
'title' => 'All posts',
'description' => 'The description of the feed.',
'language' => 'en-US',
/*
* The view that will render the feed.
*/
'view' => 'feed::atom',
/*
* The type to be used in the <link> tag
*/
'type' => 'application/atom+xml',
],
],
];
A úplně poslední, co nám zbývá, je přidání RSS cesty na konec souboru routes/web
:
use App\Http\Controllers\PostsController;
Route::get('/posts/{post}', [PostsController::class, 'show'])->name('posts.show');
Route::feeds();
Metoda Route::feeds()
je makro, které definuje nezbytné cesty z naší config/feed.php
konfigurace. Dále využíváme parametru {post}
, který se automaticky naváže podle jeho primárního klíče na danou cestu.
A to je vše, náš RSS feed by měl být zcela dostupný na /posts.rss
.
Máte dotazy, něco vám není jasné nebo mám někde chybu? Napište mi do komentářů!