spatie/laravel-url-ai-transformer
Laravel package to transform URLs and their web content with AI. Extract structured data (JSON-LD), generate summaries, images, or custom outputs via transformers and prompts. Runs via an Artisan command and stores results in the database for later retrieval.
Installation: Add the package via Composer:
composer require spatie/laravel-url-ai-transformer
Publish the config:
php artisan vendor:publish --provider="Spatie\LaravelUrlAiTransformer\UrlAiTransformerServiceProvider"
Configure AI Provider: Set your AI service (e.g., OpenAI, Anthropic) in .env:
URL_AI_TRANSFORMER_PROVIDER=openai
URL_AI_TRANSFORMER_MODEL=gpt-4
First Use Case: Transform a URL into structured data:
use Spatie\LaravelUrlAiTransformer\Support\Transform;
use Spatie\LaravelUrlAiTransformer\Transformers\LdJsonTransformer;
Transform::urls('https://example.com/blog/post')
->usingTransformers(new LdJsonTransformer);
Run the Command:
php artisan transform-urls
Retrieve Results:
$result = \Spatie\LaravelUrlAiTransformer\Models\TransformationResult::forUrl(
'https://example.com/blog/post',
'ldJson'
);
config/url-ai-transformer.php for AI provider settings.LdJsonTransformer, ImageTransformer) in vendor/spatie/laravel-url-ai-transformer/src/Transformers.transformation_results table for stored results.Register URLs:
Transform::urls('https://example.com/page1', 'https://example.com/page2')
->usingTransformers(new LdJsonTransformer);
Transform::urls(fn() => Article::published()->pluck('url')->toArray())
->usingTransformers(new LdJsonTransformer);
Run Transformations:
php artisan transform-urls (dispatches jobs).php artisan transform-urls --now
Retrieve Results:
$result = TransformationResult::forUrl('https://example.com', 'ldJson');
Scheduled Updates: Add to app/Console/Kernel.php:
protected function schedule(Schedule $schedule)
{
$schedule->command('transform-urls')->dailyAt('02:00');
}
Conditional Transformations: Override shouldRun in custom transformers:
public function shouldRun(): bool
{
return $this->urlContent->contains('important-keyword');
}
Batch Processing: Use Laravel queues with batch jobs:
Transform::urls($urls)->batch(50)->usingTransformers(new LdJsonTransformer);
API Endpoints: Create routes to fetch results:
Route::get('/transformations/{url}', function ($url) {
return TransformationResult::forUrl($url, 'ldJson')->result;
});
// app/Transformers/CustomSummaryTransformer.php
namespace App\Transformers;
use Spatie\LaravelUrlAiTransformer\Transformers\Transformer;
use Spatie\LaravelUrlAiTransformer\Support\Config;
class CustomSummaryTransformer extends Transformer
{
public function transform(): void
{
$response = Prism::text()
->using(Config::aiProvider(), Config::aiModel())
->withPrompt($this->getPrompt())
->asText();
$this->transformationResult->result = $response->text;
}
public function getPrompt(): string
{
return "Summarize this content in 5 bullet points:\n\n" . $this->urlContent;
}
public function type(): string
{
return 'custom_summary';
}
}
Rate Limits:
public $backoff = [60, 120, 300];
URL Fetching Failures:
try {
$this->transform();
} catch (\Exception $e) {
$this->transformationResult->markAsFailed($e);
}
Token Limits:
$this->urlContent = Str::limit($this->urlContent, 4000);
Transformer Type Conflicts:
MyTransformer vs. MyTransformerV2). Use explicit type():
public function type(): string { return 'my_transformer_v2'; }
Check Logs:
storage/logs/laravel.log for job failures.Inspect Database:
transformation_results for failed transformations:
SELECT * FROM transformation_results
WHERE latest_exception_seen_at IS NOT NULL;
Test Locally:
--now flag to debug synchronously:
php artisan transform-urls --now --url="https://example.com"
Mock AI Responses:
Prism in tests:
Prism::shouldReceive('text')->andReturn(new MockResponse());
Custom Job Classes:
ProcessTransformerJob to add middleware or retry logic:
// app/Jobs/CustomProcessTransformerJob.php
use Spatie\LaravelUrlAiTransformer\Jobs\ProcessTransformerJob;
class CustomProcessTransformerJob extends ProcessTransformerJob
{
public function middleware(): array
{
return [new \Illuminate\Bus\Middleware\ThrottleJobs(5)];
}
}
'process_transformer_job' => App\Jobs\CustomProcessTransformerJob::class,
Pre/Post-Processing:
beforeTransform/afterTransform in custom transformers:
public function beforeTransform(): void
{
$this->urlContent = $this->cleanContent($this->urlContent);
}
Event Listeners:
// app/Providers/EventServiceProvider.php
protected $listen = [
\Spatie\LaravelUrlAiTransformer\Events\TransformationCompleted::class => [
\App\Listeners\LogTransformation::class,
],
];
Filtering:
# Transform only URLs matching a pattern
php artisan transform-urls --url="*/blog/*"
# Transform only specific transformer types
php artisan transform-urls --transformer="ldJson"
AI Provider Switching:
.env matches the config:
URL_AI_TRANSFORMER_PROVIDER=openai
URL_AI_TRANSFORMER_MODEL=gpt-4
Spatie\LaravelUrlAiTransformer\Contracts\AiProvider.Queue Connections:
queue_connection in config to avoid default queue delays:
'queue_connection' => 'redis',
Storage Paths:
ImageTransformer, ensure storage/app/public/transformed-images is writable.Batch Processing:
--batch flag to limit concurrent jobs:
php artisan transform-urls --batch=20
Cache Results:
TransformationResult queries:
$result = Cache::remember("transform_{$url}_{$type}", now()->addHours(1), function() use ($url, $type) {
return TransformationResult::forUrl($url, $type);
});
Optimize Prompts:
public function getPrompt(): string
{
return "Extract key points from this text (max 3 sentences):\n\n" . $this->urlContent;
}
How can I help you explore Laravel packages today?