-
-
Notifications
You must be signed in to change notification settings - Fork 1
Framework Integration
Integration guides for Laravel, Symfony, and plain PHP applications.
Laravel 5.5+ automatically discovers the service provider. No manual registration needed!
Add to config/app.php:
'providers' => [
// Other providers...
Rumenx\PhpChatbot\Adapters\Laravel\PhpChatbotServiceProvider::class,
],# Publish configuration file
php artisan vendor:publish --provider="Rumenx\PhpChatbot\Adapters\Laravel\PhpChatbotServiceProvider"
# Publish specific components
php artisan vendor:publish --provider="Rumenx\PhpChatbot\Adapters\Laravel\PhpChatbotServiceProvider" --tag=config
php artisan vendor:publish --provider="Rumenx\PhpChatbot\Adapters\Laravel\PhpChatbotServiceProvider" --tag=views
php artisan vendor:publish --provider="Rumenx\PhpChatbot\Adapters\Laravel\PhpChatbotServiceProvider" --tag=assetsAfter publishing, edit config/phpchatbot.php:
<?php
return [
'model' => env('PHPCHATBOT_MODEL', 'openai'),
'api_key' => env('OPENAI_API_KEY'),
'model_name' => env('PHPCHATBOT_MODEL_NAME', 'gpt-3.5-turbo'),
'prompt' => 'You are a helpful assistant for our website.',
'temperature' => 0.7,
'max_tokens' => 1000,
// Laravel-specific features
'use_laravel_cache' => true,
'use_laravel_queue' => false,
'use_laravel_logging' => true,
'middleware' => ['web', 'throttle:60,1'],
'route' => [
'prefix' => 'api/chatbot',
'middleware' => ['api'],
'name' => 'chatbot.',
],
];Add to routes/web.php or routes/api.php:
use Illuminate\Http\Request;
use Rumenx\PhpChatbot\PhpChatbot;
Route::post('/chatbot/message', function (Request $request) {
$chatbot = app('phpchatbot');
$response = $chatbot->ask($request->input('message'));
return response()->json(['reply' => $response]);
})->middleware(['throttle:10,1']);Create app/Http/Controllers/ChatbotController.php:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Rumenx\PhpChatbot\PhpChatbot;
use Rumenx\PhpChatbot\Models\ModelFactory;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Cache;
class ChatbotController extends Controller
{
private PhpChatbot $chatbot;
public function __construct()
{
$config = config('phpchatbot');
$model = ModelFactory::make($config);
$this->chatbot = new PhpChatbot($model, $config);
}
public function message(Request $request): JsonResponse
{
$request->validate([
'message' => 'required|string|max:2000',
'conversation_id' => 'nullable|string|max:100',
]);
$message = $request->input('message');
$conversationId = $request->input('conversation_id');
try {
// Add conversation context
$context = [
'conversation_id' => $conversationId,
'user_id' => auth()->id(),
'ip_address' => $request->ip(),
'user_agent' => $request->userAgent(),
'logger' => Log::channel('chatbot'),
];
$response = $this->chatbot->ask($message, $context);
// Log conversation
Log::info('Chatbot conversation', [
'user_id' => auth()->id(),
'message' => $message,
'response' => $response,
'conversation_id' => $conversationId,
]);
return response()->json([
'reply' => $response,
'conversation_id' => $conversationId,
'timestamp' => now()->toISOString(),
]);
} catch (\Exception $e) {
Log::error('Chatbot error', [
'message' => $e->getMessage(),
'user_id' => auth()->id(),
'input' => $message,
]);
return response()->json([
'error' => 'Sorry, I encountered an error. Please try again.',
], 500);
}
}
public function history(Request $request): JsonResponse
{
$conversationId = $request->input('conversation_id');
// Retrieve conversation history from cache/database
$history = Cache::get("chat_history_{$conversationId}", []);
return response()->json(['history' => $history]);
}
}Add to routes/api.php:
use App\Http\Controllers\ChatbotController;
Route::prefix('chatbot')->middleware(['auth:sanctum'])->group(function () {
Route::post('/message', [ChatbotController::class, 'message']);
Route::get('/history', [ChatbotController::class, 'history']);
});Create custom middleware app/Http/Middleware/ChatbotMiddleware.php:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
class ChatbotMiddleware
{
public function handle(Request $request, Closure $next)
{
// Rate limiting
$key = 'chatbot:' . $request->ip();
if (RateLimiter::tooManyAttempts($key, 10)) {
return response()->json([
'error' => 'Too many requests. Please wait before trying again.'
], 429);
}
RateLimiter::hit($key, 60); // 60 seconds window
// Input validation
$message = $request->input('message');
if (strlen($message) > 2000) {
return response()->json([
'error' => 'Message too long. Please keep it under 2000 characters.'
], 422);
}
return $next($request);
}
}Create app/Providers/ChatbotServiceProvider.php:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Rumenx\PhpChatbot\PhpChatbot;
use Rumenx\PhpChatbot\Models\ModelFactory;
class ChatbotServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton('phpchatbot', function ($app) {
$config = config('phpchatbot');
// Add Laravel-specific context
$config['framework'] = 'laravel';
$config['version'] = app()->version();
$model = ModelFactory::make($config);
return new PhpChatbot($model, $config);
});
}
public function boot()
{
// Publish configuration
$this->publishes([
__DIR__.'/../../config/phpchatbot.php' => config_path('phpchatbot.php'),
], 'config');
// Publish views
$this->publishes([
__DIR__.'/../../resources/views' => resource_path('views/vendor/phpchatbot'),
], 'views');
}
}Create resources/views/chatbot/popup.blade.php:
<div id="chatbot-container" class="chatbot-container">
<div id="chatbot-popup" class="chatbot-popup" style="display: none;">
<div class="chatbot-header">
<h4>{{ config('phpchatbot.ui.title', 'Chat Support') }}</h4>
<button id="chatbot-close">×</button>
</div>
<div id="chatbot-messages" class="chatbot-messages">
<div class="chatbot-message bot-message">
{{ config('phpchatbot.ui.greeting', 'Hello! How can I help you?') }}
</div>
</div>
<div class="chatbot-input">
<input type="text" id="chatbot-input" placeholder="{{ config('phpchatbot.ui.placeholder', 'Type your message...') }}">
<button id="chatbot-send">{{ config('phpchatbot.ui.send_button', 'Send') }}</button>
</div>
</div>
<button id="chatbot-toggle" class="chatbot-toggle">💬</button>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const chatbot = {
conversationId: null,
init() {
this.bindEvents();
this.conversationId = this.generateConversationId();
},
bindEvents() {
document.getElementById('chatbot-toggle').addEventListener('click', this.toggle);
document.getElementById('chatbot-close').addEventListener('click', this.close);
document.getElementById('chatbot-send').addEventListener('click', this.sendMessage);
document.getElementById('chatbot-input').addEventListener('keypress', (e) => {
if (e.key === 'Enter') this.sendMessage();
});
},
toggle() {
const popup = document.getElementById('chatbot-popup');
popup.style.display = popup.style.display === 'none' ? 'block' : 'none';
},
close() {
document.getElementById('chatbot-popup').style.display = 'none';
},
async sendMessage() {
const input = document.getElementById('chatbot-input');
const message = input.value.trim();
if (!message) return;
this.addMessage(message, 'user');
input.value = '';
try {
const response = await fetch('/api/chatbot/message', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
'Authorization': 'Bearer ' + localStorage.getItem('api_token'),
},
body: JSON.stringify({
message: message,
conversation_id: this.conversationId,
}),
});
const data = await response.json();
if (data.reply) {
this.addMessage(data.reply, 'bot');
} else {
this.addMessage('Sorry, I encountered an error.', 'bot');
}
} catch (error) {
this.addMessage('Network error. Please try again.', 'bot');
}
},
addMessage(text, sender) {
const messages = document.getElementById('chatbot-messages');
const messageDiv = document.createElement('div');
messageDiv.className = `chatbot-message ${sender}-message`;
messageDiv.textContent = text;
messages.appendChild(messageDiv);
messages.scrollTop = messages.scrollHeight;
},
generateConversationId() {
return 'conv_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
};
chatbot.init();
});
</script>
@push('styles')
<link href="{{ asset('css/chatbot.css') }}" rel="stylesheet">
@endpushAdd to config/bundles.php:
<?php
return [
// Other bundles...
Rumenx\PhpChatbot\Adapters\Symfony\PhpChatbotBundle::class => ['all' => true],
];Create config/packages/phpchatbot.yaml:
phpchatbot:
model: '%env(PHPCHATBOT_MODEL)%'
api_key: '%env(OPENAI_API_KEY)%'
model_name: '%env(PHPCHATBOT_MODEL_NAME)%'
prompt: 'You are a helpful assistant for our Symfony application.'
temperature: 0.7
max_tokens: 1000
# Symfony-specific features
use_symfony_cache: true
use_symfony_logger: true
cache_pool: 'app.cache.chatbot'
security:
csrf_protection: true
session_validation: true
routing:
prefix: '/api/chatbot'
requirements:
_locale: 'en|es|fr'Add to config/services.yaml:
services:
# Chatbot service
Rumenx\PhpChatbot\PhpChatbot:
arguments:
$config: '%phpchatbot%'
tags: ['controller.service_arguments']
# Custom chatbot service
App\Service\ChatbotService:
arguments:
$chatbot: '@Rumenx\PhpChatbot\PhpChatbot'
$logger: '@logger'
$cache: '@cache.app'
tags: ['monolog.logger', { channel: 'chatbot' }]Create src/Controller/ChatbotController.php:
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\RateLimiter\RateLimiterFactory;
use Rumenx\PhpChatbot\PhpChatbot;
use Psr\Log\LoggerInterface;
#[Route('/api/chatbot', name: 'chatbot_')]
class ChatbotController extends AbstractController
{
public function __construct(
private PhpChatbot $chatbot,
private LoggerInterface $logger,
private RateLimiterFactory $chatbotLimiter
) {}
#[Route('/message', name: 'message', methods: ['POST'])]
public function message(Request $request): JsonResponse
{
// Rate limiting
$limiter = $this->chatbotLimiter->create($request->getClientIp());
if (!$limiter->consume()->isAccepted()) {
return $this->json(['error' => 'Rate limit exceeded'], 429);
}
$data = json_decode($request->getContent(), true);
$message = $data['message'] ?? '';
$conversationId = $data['conversation_id'] ?? null;
if (empty($message) || strlen($message) > 2000) {
return $this->json(['error' => 'Invalid message'], 400);
}
try {
$context = [
'conversation_id' => $conversationId,
'user_id' => $this->getUser()?->getId(),
'session_id' => $request->getSession()->getId(),
'locale' => $request->getLocale(),
'logger' => $this->logger,
];
$response = $this->chatbot->ask($message, $context);
$this->logger->info('Chatbot conversation', [
'user_id' => $this->getUser()?->getId(),
'message' => $message,
'response' => $response,
'conversation_id' => $conversationId,
]);
return $this->json([
'reply' => $response,
'conversation_id' => $conversationId,
'timestamp' => (new \DateTime())->format(\DateTime::ATOM),
]);
} catch (\Exception $e) {
$this->logger->error('Chatbot error', [
'message' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
return $this->json(['error' => 'Internal error'], 500);
}
}
#[Route('/history/{conversationId}', name: 'history', methods: ['GET'])]
public function history(string $conversationId): JsonResponse
{
// Implement conversation history retrieval
return $this->json(['history' => []]);
}
}Create src/EventSubscriber/ChatbotSubscriber.php:
<?php
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class ChatbotSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
KernelEvents::REQUEST => 'onKernelRequest',
];
}
public function onKernelRequest(RequestEvent $event): void
{
$request = $event->getRequest();
// Add chatbot-specific headers or processing
if (str_starts_with($request->getPathInfo(), '/api/chatbot')) {
$request->headers->set('X-Chatbot-Request', 'true');
}
}
}Create templates/chatbot/widget.html.twig:
<div id="chatbot-widget" data-config="{{ chatbot_config|json_encode }}">
<div id="chatbot-popup" class="chatbot-popup" style="display: none;">
<div class="chatbot-header">
<h4>{{ 'chatbot.title'|trans }}</h4>
<button id="chatbot-close" class="chatbot-close">×</button>
</div>
<div id="chatbot-messages" class="chatbot-messages">
<div class="chatbot-message bot-message">
{{ 'chatbot.greeting'|trans }}
</div>
</div>
<div class="chatbot-input">
<input type="text"
id="chatbot-input"
placeholder="{{ 'chatbot.placeholder'|trans }}">
<button id="chatbot-send">{{ 'chatbot.send'|trans }}</button>
</div>
</div>
<button id="chatbot-toggle" class="chatbot-toggle">
💬
</button>
</div>
{% block javascripts %}
{{ parent() }}
<script src="{{ asset('js/chatbot-symfony.js') }}"></script>
{% endblock %}Create src/Chatbot/ChatbotManager.php:
<?php
namespace App\Chatbot;
use Rumenx\PhpChatbot\PhpChatbot;
use Rumenx\PhpChatbot\Models\ModelFactory;
class ChatbotManager
{
private PhpChatbot $chatbot;
private array $config;
public function __construct(string $configPath = null)
{
$configPath = $configPath ?: __DIR__ . '/../../config/phpchatbot.php';
$this->config = require $configPath;
$model = ModelFactory::make($this->config);
$this->chatbot = new PhpChatbot($model, $this->config);
}
public function handleRequest(): void
{
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit;
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Method not allowed']);
exit;
}
$this->processMessage();
}
private function processMessage(): void
{
try {
// Rate limiting
$this->checkRateLimit();
// Get input
$input = json_decode(file_get_contents('php://input'), true);
$message = $input['message'] ?? '';
if (empty($message) || strlen($message) > 2000) {
throw new \Exception('Invalid message');
}
// Process message
$context = [
'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
'timestamp' => date('Y-m-d H:i:s'),
];
$response = $this->chatbot->ask($message, $context);
// Log conversation
$this->logConversation($message, $response, $context);
echo json_encode([
'reply' => $response,
'timestamp' => date('c'),
]);
} catch (\Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
// Log error
error_log("Chatbot error: " . $e->getMessage());
}
}
private function checkRateLimit(): void
{
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
$key = 'chatbot_rate_' . md5($ip);
$limit = $this->config['rate_limit'] ?? 10;
$window = $this->config['rate_window'] ?? 60;
// Simple file-based rate limiting
$cacheFile = sys_get_temp_dir() . '/' . $key;
$now = time();
if (file_exists($cacheFile)) {
$data = json_decode(file_get_contents($cacheFile), true);
$data = array_filter($data, fn($time) => $now - $time < $window);
if (count($data) >= $limit) {
throw new \Exception('Rate limit exceeded');
}
$data[] = $now;
} else {
$data = [$now];
}
file_put_contents($cacheFile, json_encode($data));
}
private function logConversation(string $message, string $response, array $context): void
{
if (!($this->config['enable_logging'] ?? true)) {
return;
}
$logEntry = [
'timestamp' => date('Y-m-d H:i:s'),
'ip' => $context['ip_address'],
'message' => $message,
'response' => $response,
];
$logFile = $this->config['log_file'] ?? 'chatbot.log';
file_put_contents($logFile, json_encode($logEntry) . "\n", FILE_APPEND);
}
}Create public/api/chatbot.php:
<?php
require_once __DIR__ . '/../../vendor/autoload.php';
use App\Chatbot\ChatbotManager;
try {
$chatbot = new ChatbotManager();
$chatbot->handleRequest();
} catch (\Exception $e) {
http_response_code(500);
echo json_encode(['error' => 'Server error']);
error_log("Chatbot initialization error: " . $e->getMessage());
}Create public/js/chatbot.js:
class PlainPHPChatbot {
constructor(options = {}) {
this.apiUrl = options.apiUrl || '/api/chatbot.php';
this.container = options.container || 'chatbot-container';
this.init();
}
init() {
this.createHTML();
this.bindEvents();
}
createHTML() {
const container = document.getElementById(this.container);
container.innerHTML = `
<div id="chatbot-popup" class="chatbot-popup" style="display: none;">
<div class="chatbot-header">
<h4>Chat Support</h4>
<button id="chatbot-close">×</button>
</div>
<div id="chatbot-messages" class="chatbot-messages">
<div class="chatbot-message bot-message">
Hello! How can I help you today?
</div>
</div>
<div class="chatbot-input">
<input type="text" id="chatbot-input" placeholder="Type your message...">
<button id="chatbot-send">Send</button>
</div>
</div>
<button id="chatbot-toggle" class="chatbot-toggle">💬</button>
`;
}
bindEvents() {
document.getElementById('chatbot-toggle').onclick = () => this.toggle();
document.getElementById('chatbot-close').onclick = () => this.close();
document.getElementById('chatbot-send').onclick = () => this.sendMessage();
document.getElementById('chatbot-input').onkeypress = (e) => {
if (e.key === 'Enter') this.sendMessage();
};
}
toggle() {
const popup = document.getElementById('chatbot-popup');
popup.style.display = popup.style.display === 'none' ? 'block' : 'none';
}
close() {
document.getElementById('chatbot-popup').style.display = 'none';
}
async sendMessage() {
const input = document.getElementById('chatbot-input');
const message = input.value.trim();
if (!message) return;
this.addMessage(message, 'user');
input.value = '';
try {
const response = await fetch(this.apiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message })
});
const data = await response.json();
if (data.reply) {
this.addMessage(data.reply, 'bot');
} else if (data.error) {
this.addMessage('Error: ' + data.error, 'bot error');
}
} catch (error) {
this.addMessage('Network error. Please try again.', 'bot error');
}
}
addMessage(text, className) {
const messages = document.getElementById('chatbot-messages');
const div = document.createElement('div');
div.className = `chatbot-message ${className}`;
div.textContent = text;
messages.appendChild(div);
messages.scrollTop = messages.scrollHeight;
}
}
// Initialize when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
new PlainPHPChatbot();
});<?php
namespace App\Controllers;
use CodeIgniter\RESTful\ResourceController;
use Rumenx\PhpChatbot\PhpChatbot;
use Rumenx\PhpChatbot\Models\ModelFactory;
class Chatbot extends ResourceController
{
protected $format = 'json';
public function message()
{
$config = config('Chatbot');
$model = ModelFactory::make($config);
$chatbot = new PhpChatbot($model, $config);
$message = $this->request->getJSON()->message ?? '';
try {
$response = $chatbot->ask($message);
return $this->respond(['reply' => $response]);
} catch (\Exception $e) {
return $this->respond(['error' => $e->getMessage()], 500);
}
}
}<?php
namespace App\Controller;
use Cake\Controller\Controller;
use Rumenx\PhpChatbot\PhpChatbot;
use Rumenx\PhpChatbot\Models\ModelFactory;
class ChatbotController extends Controller
{
public function initialize(): void
{
parent::initialize();
$this->loadComponent('RequestHandler');
}
public function message()
{
$this->request->allowMethod(['post']);
$config = Configure::read('Chatbot');
$model = ModelFactory::make($config);
$chatbot = new PhpChatbot($model, $config);
$message = $this->request->getData('message');
try {
$response = $chatbot->ask($message);
$this->set(['reply' => $response]);
$this->viewBuilder()->setOption('serialize', ['reply']);
} catch (\Exception $e) {
$this->response = $this->response->withStatus(500);
$this->set(['error' => $e->getMessage()]);
$this->viewBuilder()->setOption('serialize', ['error']);
}
}
}<?php
namespace App\Adapters;
use Rumenx\PhpChatbot\Contracts\AdapterInterface;
use Rumenx\PhpChatbot\PhpChatbot;
class CustomFrameworkAdapter implements AdapterInterface
{
private $framework;
public function __construct($framework)
{
$this->framework = $framework;
}
public function register(PhpChatbot $chatbot): void
{
// Register chatbot with your framework
$this->framework->singleton('chatbot', function() use ($chatbot) {
return $chatbot;
});
}
public function getConfig(): array
{
// Load configuration from your framework
return $this->framework->config('chatbot');
}
public function log(string $message, array $context = []): void
{
// Use your framework's logging
$this->framework->log()->info($message, $context);
}
public function cache(string $key, $value, int $ttl = 3600): void
{
// Use your framework's caching
$this->framework->cache()->put($key, $value, $ttl);
}
}Next: Frontend Integration