spatie/laravel-there-there
Expose your Laravel app data as JSON for There There. Configure a secret and endpoint, validate incoming requests, and register a sidebar callback to return relevant customer info when agents open a ticket, shown in There There’s sidebar.
Installation:
composer require spatie/laravel-there-there
Publish the config file:
php artisan vendor:publish --provider="Spatie\ThereThere\ThereThereServiceProvider"
Configure Webhook:
Add the There There webhook URL to your config/there-there.php:
'webhook_url' => env('THERE_THERE_WEBHOOK_URL'),
First Use Case:
Expose a simple model (e.g., User) by implementing Spatie\ThereThere\ThereThereable:
use Spatie\ThereThere\ThereThereable;
class User implements ThereThereable
{
public function getThereThereData(): array
{
return [
'name' => $this->name,
'email' => $this->email,
'created_at' => $this->created_at->toDateTimeString(),
];
}
}
Trigger Webhook:
When a ticket is created in There There, it will call your endpoint (e.g., /there-there/{model}/{id}) to fetch data.
Model Integration:
ThereThereable on Eloquent models to define exposed data.getThereThereData() to return a structured array of attributes.Dynamic Data Fetching:
getThereThereData():
public function getThereThereData(): array
{
return [
'orders' => $this->orders()->with(['customer'])->get()->map(fn ($order) => [
'id' => $order->id,
'customer_name' => $order->customer->name,
]),
];
}
API Endpoint:
/there-there/{model}/{id}) to handle webhook calls.routes/there-there.php.Authentication:
api or custom):
Route::middleware(['api', 'signed'])->group(function () {
// ...
});
Testing:
$response = $this->get('/there-there/user/1');
$response->assertJson(['name' => 'John Doe']);
Conditional Data: Use closures to conditionally include data:
public function getThereThereData(): array
{
return [
'is_admin' => $this->role === 'admin' ? true : null,
];
}
Nested Resources: Expose related models recursively:
public function getThereThereData(): array
{
return [
'project' => $this->project->load(['members'])->getThereThereData(),
];
}
Caching: Cache responses for performance (e.g., using Laravel's cache):
public function getThereThereData(): array
{
return Cache::remember("there-there-{$this->id}", now()->addMinutes(5), function () {
return [...];
});
}
Webhook URL Mismatch:
webhook_url in config/there-there.php matches the There There app's configured URL.Missing Middleware:
api, signed, or custom).Circular References:
getThereThereData() calls can cause infinite loops.->first() or limit depth:
public function getThereThereData(): array
{
return [
'parent' => $this->parent?->getThereThereData(),
];
}
Performance Issues:
getThereThereData() can slow responses.with():
$this->load(['comments.user']);
Model Not Found:
public function getThereThereData(): array
{
if (!$this->exists) {
return ['error' => 'Model not found'];
}
return [...];
}
Log Webhook Calls: Add logging to debug incoming requests:
\Log::info('There There webhook called for', ['model' => $model, 'id' => $id]);
Test Locally: Use tools like ngrok to test webhook calls locally:
ngrok http 8000
Configure the There There webhook URL to point to your ngrok endpoint.
Validate JSON:
Ensure getThereThereData() returns valid JSON:
public function getThereThereData(): array
{
return json_decode($this->json_data, true) ?? [];
}
Custom Endpoint Logic: Override the default route behavior by binding a custom controller:
Route::get('/there-there/{model}/{id}', [ThereThereController::class, 'show']);
Add Metadata: Extend the response with metadata (e.g., ticket ID):
public function getThereThereData(): array
{
return [
'_metadata' => [
'ticket_id' => request('ticket_id'),
],
// ...
];
}
Dynamic Model Resolution: Resolve models dynamically (e.g., for polymorphic relationships):
public function getThereThereData(): array
{
$model = $this->morphTo()->firstOrFail();
return $model->getThereThereData();
}
Localization: Support multiple languages by returning localized data:
public function getThereThereData(): array
{
return [
'name' => __("user.{$this->locale}.name", ['name' => $this->name]),
];
}
How can I help you explore Laravel packages today?